1、进程的定义
在某种程度上, 可以将应用程序看成是一个进程,其将会消耗耕种各样的计算机资源。
定义:
一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程。
只有当操作系统把执行程序调入到我们的内存之后,让这个程序可以执行起来。(能够让通过cpu对这个程序执行一条条的指令,读取数据完成一定的功能)。也就是静态的执行程序,通过cpu变成一个动态的执行过程,而这个动态的执行过程就是进程。
整个的功能是由程序的代码决定的。
2、进程的组成
1、一个进程应该包括
- 程序的代码
- 程序处理的数据
- 程序计数器中的值,指示下一条将运行的指令
- 一组通用的寄存器的当前值,堆,栈
- 一组系统资源(如打开的文件)
总之,进程包含了正在运行的一个程序的所以状态信息。
2、进程与程序的联系
- 程序是产生进程的基础
程序是静态的代码,代码限制了进程完成是什么样的功能
- 程序的每次运行构成不同的进程
程序多次运行过程中输入的数据不一样,产生的结果是不一样的,所以构成了不一样的进程
- 进程是程序功能的体现
尽管输出可能不同,但是这个程序的功能是一样的
- 通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序。
进程和程序之间是一个多对多的关系。
3、进程与程序的区别
- 进程是动态的,程序是静态的;程序是有序代码的集合;进程是程序的执行,进程有核心态/用户态。
- 进程是暂时的,程序是永久的;进程是一个状态变化的过程,程序可长久保存。
- 进程与程序的组成不同;进程的组成包括程序、数据和进程控制块(既进程状态信息)
一个很有趣进程与cpu的类比:
- cpu在工作中会存在切换处理不同程序的
3、进程的特点
- 动态性:可动态地创建,结束进程;
- 并发性:进程间可以被独立调度并占有处理机运行;(并发与并行的区别,前者是可以为1个cpu,后者必须要多个cpu)
- 独立性:不同进程的工作不互相影响,也就是进程不会破坏代码数据的正常执行。(页表的支持)
- 制约性:因访问共享数据/资源或进程间同步而产生制约
a–体现了动态性 / b–体现了独立性 / c–体现了制约性
描述进程的数据结构:进程控制块(Process Control Block,PCB)
操作系统为每一个进程都维护了一个PCB,用来保存与该进程有关的各种状态信息和需要资源的情况等等。
4、进程控制结构
1、进程控制块:操作系统管理控制进程运行所用的信息集合。
- 操作系统用PCB来描述进程的基本情况以及运行变化的过程,PCB是进程存在的唯一标志,如果进程消失了那么其对应的PCB也会消失,是一一对应的关系。
2、使用进程控制块
- 进程的创建:为该进程生产一个PCB;
- 进程的终止:回收它的PCB;
- 进程的组织管理:通过对PCB的组织管理来实现;
问题:PCB具体包含什么信息?如何组织的?进程的状态转换…?
3、PCB包含的信息
1)进程标识信息。
如本进程的标识(进程号,执行的次数…),本进程的产生者标识(父进程标识);用户标识。
2)处理机状态信息保存区
保存进程的运行现场信息:
- 用户可见寄存器:用户程序可以使用的数据,地址等寄存器。
- 控制和状态寄存器:如程序计数器(PC),程序状态字(PSW)
- 栈指针:过程调用/系统调用/中断处理和返回时需要用到它。找到当前运行的位置。
3)进程控制信息
操作系统需要对这个进程进行管理和控制调度和
- 调度和状态信息:用于操作系统调度进程并占用处理机使用。
- 进程间通信信息:为支持进程间与通信相关的各种标识,信号,信件等。这些信息存在接受方的进程控制块中
- 存储管理信息:包含有指向本进程映射存储空间的数据结构。
- 进程所用资源:说明有进程打开、使用的系统资源,如打开的文件等。
- 有关数据结构连接信息:进程可以连接到一个进程队列中,或连接到相关的其他进程的PCB
4、PCB的组织方式
- 链表:同一状态的进程其PCB称一链表,多个状态对应多个不同的链表。各个状态的进程形成不同的链表:就绪链表,阻塞链表。
- 索引表:同一个状态的进程归入一个index表(由index指向PCB),多个状态对应多个不同的index表。各个状态的进行形成不同的索引表:就绪索引表、阻塞索引表
一般来说会采取链表,因为进程的控制是动态的插入和删除的,所以链表组织方式比较方便,而索引开销比较大。当然如果一开始就固定住了进程的数目,索引也不失为一个选择。
以上是围绕进程静态部分说明,组成,特点等等。
一下是围绕进程动态的状态特点说明,有3个方面的内容:
- 进程的生命周期管理
- 进程状态变化模型
- 进程挂起模型
【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!)
内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料
5、进程的生命期管理
进程的生命是指进程的创建到结束这么一整个的生命期。
进程的生命期管理有以下几个时期:
- 进程创建
- 进程运行:正在占用cpu,执行这个进程
- 进程等待:由于某种特殊原因需要等待
- 进程唤醒:当等待的条件满足,需要唤醒
- 进程结束
1、进程创建
引起进程创建的3个主要事件:
1)系统初始化时
2)用户请求创建一个新进程
3)正在运行的进程执行了创建进程的系统调用
但是创建了新的进程不一定可以执行。
2、进程运行
内核选择一个就绪的进程,让它占用处理机并执行
就绪态----->执行态
其中涉及两个问题:
1)为何选择?
2)如何选择?(涉及调度算法)
3、进程等待
在以下情况下,进程等待(阻塞):
1)请求并等待系统服务,无法马上完成
2)启动某种操作,无法马上完成
3)需要的数据没有到达
ps:进程等待事件的发起是有自己发起的。因为进程只能自己阻塞自己,因为只有进程自身才能知道何时需要等待某种事件的发生。
4、进程唤醒
唤醒进程的原因:
1) 被阻塞进程需要的资源可被满足
2) 被阻塞进程等待的事件到达
3) 将该进程的PCB插入到就绪队列
ps:进程只能被别的进程或者操作系统唤醒
5、进程结束
在以下四种情况下,进程结束
1)正常退出(自愿的)
2)错误退出(自愿的)
3)致命错误(操作系统强制性的)
4)被其他进程所杀(强制性的)
6、进程状态变化模型
1、进程的三种基本状态:
进程在生命结束前处于且仅处于三种基本状态之一,不同系统设置的进程状态数目不同。
1)运行状态(Running):当一个进程正在处理机上运行时。
2)就绪状态(Ready):一个进程获得了除处理机之外的一切所需资源。一旦得到处理机即可运行。
3)等待状态(又称阻塞状态Blocked):一个进程正在等待某一事件而暂停运行时。如等待某资源,等待输入/输出完成。
2、三状态变化图:
进程其他的基本状态:
- 创建状态(New):一个进程正在被创建,还没被转到就绪状态之前的状态。
- 结束状态(Exit):一个进程正在从系统中消失时的状态,这是因为进程结束或由于其他原因所导致。
3、五状态变化图:
就绪态的出现是由于调度机制的存在。
4、可能的状态变化如下:
- NULL->New: 一个新进程被产生出来执行一个程序。
- New->Ready: 当进程被创建完成并初始化后,一切就绪准备运行时,变为就绪状态。(不会持续很久,也就只是一个PCB的初始化。)
- Ready->Running :处于就绪状态的进程被进程调度程序选中后,就分配到处理机上来运行。
- Running->Exit :当进程表示它已经完成或出现错误,当前运行进程会有操作系统作结束处理。
- Running->Ready :处于运行状态的进程在其运行过程中,由于分配给它的处理机时间片用完而让出处理机。(操作系统完成)
- Runing->Blocked :当进程请求某样东西切必须等待时。(例如等待一个定时器的到达,读写文件,因为过程比较慢)
- Blocked->Ready :当进程要等待某事件到来时,它从阻塞状态变到就绪状态。(同样由操纵系统完成)
7、进程的挂起
进程挂起和进程阻塞的不一样的。
进程在挂起状态意味着进程没有占有内存空间。处于挂起状态的进程影像在磁盘上。
1、挂起状态
1)阻塞挂起状态(Blocked-suspend):进程在外存并等待某事件的出现;
2)就绪挂起状态(Ready-suspend):进程在外存,但只要进入内存,即可运行。
2、与挂起中相关的状态转换
挂起(Suspend):把一个进程从内存转到外存;可能有以下几种情况:
1)阻塞到阻塞挂起:没有进程处于就绪状态或就绪进程要求更多内存资源时,会进行这种转换,以提高新进程或运行就绪进程;
2)就绪到就绪挂起:当有高优先级阻塞(系统认为会很快就绪的)进程和低优先级就绪进程时,系统会选择挂起低优先级就绪进程;
3)运行到就绪挂起:对抢先式分时系统,当有高优先级阻塞挂起进程因事件出现(空间不够)而进入就绪挂起,系统可能会把运行进程转到就绪挂起状态。
- 在外存时的状态转换:
阻塞挂起到就绪挂起:当有阻塞挂起进程相关事件出现时(也就是条件满足),系统会把阻塞挂起进程转换到就绪挂起进程。
- 解挂/激活(Activate):把一个进程从外存转到内存;可能有以下几种情况:
1)就绪挂起到就绪:没有就绪进程或挂起就绪进程优先级高于就绪进程时,会进行这种转换。
2)阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起(系统认为会很快出现所等待的事件)进程转换为阻塞进程。
问题:操作系统怎么通过PCB的定义的进程状态来管理PCB,帮助完成进程的调度过程?
以进程为基本结构的os,选择某一个进程变成某一种状态都是有操作系统来完成的。最底层为CPU调度程序(包括中断处理等)。上面一层为一组各式各样的进程。
3、状态队列
状态队列是操作系统管理进程的一个很重要的数据结构
1)由操作系统来维护一组队列,用来表示系统当中所以进程的当前状态;
2)不同的状态分别用不同的队列来表示(就绪队列,各种类型的阻塞队列);
3)每个进程的PCB都根据它的状态加入到相应的队列当中,当一个进程的状态发生变化时,它的PCB从一个状态队列中脱离出来,加入到另外一个队列。
4、状态表示方法
要注意,如果事件1只能满足一个进程,那么只能把这一个进程从阻塞态变成就绪态。如果事件1产生之后,所以等待事件1的进程都等到满足,那么这些进程都会挂到就绪队列里面去。
线程管理:
很久之前,操作系统一直以进程作为独立运行的基本单位,直到80年代中期,人们有提出了更小独立运行的基本单位—线程。
1)为什么使用线程?
2)什么是线程
3)线程的实现
4)多线程编程接口举例
8、为什么使用线程
例子:
1、单进程的实现方法
可能出现的问题:
1)播放出来的声音能否连续?
2)各个函数之间不是并发执行,影响资源的使用效率。
2、多进程的实现方法
可能出现的问题:
1)进程之前如何通信,共享数据?
2)维护进程的系统开销比较大:
创建进程时,分配资源,建立PCB;撤销进程时,回收资源,撤销PCB;进程切换时,保存当前进程的状态信息。
根据以上问题,提出一个新的实体,满足一下特性:
1)实体之间可以并发地执行;
2)实体之间共享相同的地址空间;
这个实体就是:线程(Thread)
9、什么是线程
1、定义:进程当中的一条执行流程
2、从两个方面来重新理解进程
1)从资源组合的角度:
进程把一组相关的资源组合起来,构成了一个资源平台(环境),包括地址空间(代码段,数据段)、打开的文件等各种资源;
2)从运行的角度:
代码在这个资源平台上的一条执行流程(线程)。
ps:进程中的堆,代码段,数据段是线程所共享的内容。而各自又有独特的内容,比如所堆栈,程序计数器,寄存器(不同的执行留和控制流)。所以其有独立拥有的部分,也有公有的部分。
3、线程 = 进程 - 共享资源
线程的优点:
1)一个进程中可以同时存在多个线程
2)各个线程之间可以并发地执行
3)各个线程之间可以共享地址空间和文件等资源
线程的缺点:
一个线程奔溃,会导致其所属进程的所以线程奔溃,安全没有一定的保障。
4、不同操作系统对线程的支持
5、线程所需的资源
6、线程与进程的比较
1)进程是资源分配单位,线程是CPU调度;
2)进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈;
3)线程同样具有就绪、阻塞和执行三种基本状态,同样具有状态之间的转换关系;
4)线程能减少并发执行的时间和空间开销;
- 线程的创建时间比进程短;
- 线程的终止时间比进程短;
- 同一进程内的线程切换时间比进程短;
- 由于同一进程的各线程间共享内存和文件资源,可直接进行不通过内核的通信。
(切换进程的时候,需要把页表也切换掉,切换页表的开销比较大,因为硬件的信息无效,需要重新加载)
10、线程的实现
主要有三种线程的实现方式:
1)用户线程:在用户空间实现;
2)内核线程:在内核中实现;
3)轻量级进程:在内核汇总实现,支持用户线程
用户线程:操作系统看不到的线程称为用户线程
内核线程:操作系统管理起来(能够看见)的线程称为内核线程
1、用户线程与内核线程的对应关系
1)多对一
2)一对一
3)多对多
2、用户进程
线程控制块(TCB)是在库里面实现的,对于操作系统而言,其看不见TCB,只能看见进程的信息,但是进程里面的线程信息,是有线程管理的库来实现的。
在用户空间实现的线程机制,它不依赖与操作系统的内核,由一组用户级的线程库函数来完成线程的管理,包括进程的创建,终止,同步和调度等。
- 由于用户线程的维护由相应进程来完成(通过线程库函数),不需要操作系统内核了解用户线程的存在,可用于不支持线程技术的多进程操作系统;
- 每个进程都需要它自己私有的线程控制块(TCB)列表,用来跟踪记录它的各个线程的状态信息(PC,栈指针,寄存器),TCB由线程库函数来维护;
- 用户线程的切换也是由线程库函数来完成,无需用户态/核心态切换,所以速度特别快;
- 允许每个进程拥有自定义的线程调度算法。
否则如果进程被操作系统调度为阻塞态,则其下的所有线程都无法允许。
用户线程的缺点:
1)阻塞性的系统调用如何实现?如果一个线程发起系统调用而阻塞,则整个进程在等待。因为操作系统只能看见进程,所以这个进程阻塞,旗下所以的线程都会阻塞。
2)当一个线程开始执行后,除非它主动地交出CPU的使用权,否则它所在的进程当中的其他线程将无法运行。
3)由于时间片分配给进程,故与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会较慢。
3、内核线程
(操作系统看得见,TCB是放在内核里面的)
内核线程是指在操作系统的内核当中实现的一种线程机制,由操作系统的内核来完成线程的创建,终止和管理。
- 在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息(PCB和TCB);
- 线程的创建,终止和切换都是通过系统调用/内核函数的方式来进行,由内核来完成,因此系统开销较大;
- 在一个进程当中,如果某个内核线程发起系统调用而被阻塞,并不影响其他内核线程的运行;
- 时间片分配给线程,多线程的进程获得更多的cpu时间;
- Windows NT和Windows 200/XP支持内核线程
4、轻量级进程(LightWeight Process)
它是内核支持的用户线程,一个进程可有一个或多个轻量级进程,每个轻量级进程有一个单独的内核线程来支持,(Solaris/Linux)
11、上下文切换
1、定义:停止当前运行的进程(从运行状态改变成其他状态)并且调度其他进程(转变成运行状态的)的过程,称为进程的上下文切换(Compress)
- 必须在切换之前存储许多部分的进程上下文
- 必须能够在之后恢复他们,所以进程不能显示它曾经被暂停过
- 必须快速(上下文准换是非常频繁的)
进程的上下文切换所具体切换的进程所用到的寄存器,使用要关注cpu有哪些寄存器(PC程序计数器,SP堆栈指针)。而在做进程切换的时候,需要将这新信息保存到进程控制块的某一个地方上。
2、切换的过程
所用的信息都是和硬件紧密相连的,所用一般是使用汇编代码来完成编写。
操作系统为活跃进程准备了进程控制块(PCB)
操作系统将进程控制块(PCB)放置在一个合适的队列里
- 就绪队列
- 等待I/O队列(每个设备的队列)
- 僵尸队列
12、进程控制—创建进程
window系统下:
linux系统下:
fork()创建一个继承的子进程
- 复制父进程的所有变量和内存
- 复制父进程的所以CPU寄存器(有一个寄存器除外)
fork()的返回值
- 子进程的fork()返回0
- 父进程的fork()返回子进程标识符
- fork()返回值可方便后续使用,子进程可使用getpid()获取PID
父子进程的主要区别----childPID不一样:
13、进程控制—加载和执行过程
系统调用exec()加载程序取代当前运行的进程
其中:
exec是准备执行一个新的程序,所以当成功的执行了exec之后,后面的printf函数是不会执行到的。
wait(pid); wait返回了,表示子进程就结束了。
当执行exec的时候,代码数据都复制了一份,但是PID没有变化。但是执行的代码改变了,也就是另外程序的执行过程,如下所示。
执行exec之后,程序的整个控制流会放生完全的变化
1)exec()调用允许一个进程“加载”一个不同的程序并且在main开始执行(事实上_start,系统调用)
2)它允许1一个进程指定参数的数量(argc)和它字符串参数数组(argv)
3)代码段,数据段,stack(栈)&heap(堆)都会被覆盖
fork()的简单实现
- 对子进程分配内存
- 复制父进程的内存和CPU寄存器到子进程里
在99%的情况里,我们在调用fork()之后调用exec()
- 在fork()操作中内存复制是没有作业的
- 子进程将可能关闭打开的文件和链接
对于此情况需要一个优化:
vfork(),vfork只是复制了一小部分的进程的内容,绝大多数的内容都没有被复制。但是这会使系统调用变成两个fork,增加了编程人员的开销。
另一个优化:
通过虚存管理科员实现一个高效的fork实现机制。也就是(Copy on Write,COW)技术,就是写的时候再进行复制。
当父进程创建子进程的时候,如果采用COW技术时,我们在做实际的子进程地址空间复制的时候并没有真是的复制,只是复制了父进程所需要的元数据—页表等等,实现按需写的情况来复制不同的页。
14、进程控制—等待和终止进程
wait()系统调用是被父进程用来等待子进程的结束
- 父进程先与子进程死亡—子进程为孤儿进程
- 子进程已经死亡,但父进程还没来得及回收—子进程为僵尸进程
状态转换图:
ps:执行exec的时候,程序有可能会处于不同的状态。
因为在执行exec有两个步骤,一个是加载执行程序,二个是运行执行程序。加载的时候,所需要的时间比较长,所以会处于阻塞状态。