一.进程的组成
进程的组合如下所示:
1.PCB(进程控制块)
PCB是进程存在的唯一标志,当进程被创建时,操作系统为其创建PGB,当进程结束时,会回收其PCB。操作系统对进程进行管理工作所需的信息都存在PCB中。
(1)进程描述信息
进程描述信息包括进程标识符PID以及用户表示符UID,PID为进程ID,当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的“身份证号”,即PID,例如:区别下面三个进程的就是PID。
(2)进程控制和管理信息
例如CPU,磁盘,网络流量使用情况,以及进程当前状态:就绪态/阻塞态/运行态,从任务管理前期我们就能看到进程相关信息
(3)资源分配清单
资源分配清单指的是系统正在使用哪些文件,内存区域或者I/O设备
(4)处理机相关信息
2.程序段与数据段
PCB是给操作系统使用的,程序段与数据段是给进程自己用的,与进程自身的运行逻辑有关。
程序运行过程如下:
高级程序会形成可执行文件,程序运行前,会将其从硬盘读入内存中,一个程序开始运行前,需要创建对应的进程,也就要创建相应的PCB,除了PCB,还需要读入一系列的指令序列,这一系列的指令序列就是程序段,CPU会从内存中取出指令,并且执行这些指令。在执行指令时,会产生中间数据,例如中间变量等,这些数据就存放在数据段中
注:其实这里的进程更准确地说是进程实体(进程映像),因为进程是动态的,而进程实体是静态的。进程运行过程中,进程实体是在不断变化的,例如x=1,运行x++后,x=2
进程与进程实体的关系:
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
.进程的特征
程序是静态的,进程是动态的,相比于程序,进程拥有以下特征:
三.进程的状态以及状态的转换
进程的状态包括三种基本状态就绪态,运行态,阻塞态,创建态和终止态。
状态的转换过程如下:
1.进程正在被创建时,它的状态是“创建态”,在这个阶段操作系统会为进程分配资源、初始化PCB。
2.当进程创建完成后,便进入“就绪态”,处于就绪态的进程已经具备运行条件,但由于没有空闲CPU,也就是处理机,就暂时不能运行。
3.系统中可能会有很多个进程都处于就绪态,当CPU空闲时,操作系统就会选择一个就绪进程,
让它上处理机运行,若一个进程此时在CPU上运行,也就是CPU会执行该进程对应的程序(执行指令序列),那么这个进程处于"运行态"。
4.在进程运行的过程中,可能会请求等待某个事件的发生(如等待某种系统资源的分配,或者等待其他进程的响应),在这个事件发生之前,进程无法继续往下执行,此时操作系统会让这个进程下CPU,并让它进入“阻塞态”。当CPU空闲时,又会选择另一个“就绪态”进程上CPU运行。
5.一个进程可以执行 exit 系统调用,请求操作系统终止该进程。此时该进程会进入“终止态”操作系统会让该进程下CPU,并回收内存空间等资源,最后还要回收该进程的PCB。
进程状态转换图如下:
运行态-->阻塞态是一种进程自身做出的主动行为。
阻塞态-->就绪态不是进程自身能控制的,是一种被动行为。
注:
进程不能由阻塞态直接转换为运行态也不能由就绪态直接转换为阻塞态 (因为进入阻塞态是进程主动请求的,必然需要进程在运行时才能发出这种请求,而不能从就绪态到阻塞态)
运行态可以转化为就绪态:时间片到,或处理机被抢占。
四.进程的组织
为了对同一个状态下的各个进程进行统一的管理,操作系统会将各个进程的PCB组织起来。
1.链接方式
链接方式表示操作系统按照进程状态将PCB分为很多队列,操作系统持有指向各个队列的指针,每个队列都会指向相应状态进程的PCB
很多操作系统还会根据阻塞原因不同,再分为多个阻塞队列
2.索引方式
操作系统会根据进程状态不同,为进程建立索引表,操作系统持有指向各个索引表的指针,每个索引表的表项会指向相应的PCB
五.进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,也就是说进程控制就是要实现进程状态转换,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。
如何实现进程控制:
进程控制的实现是通过原语实现的:
原语在操作系统的内核中,原语是一种特殊的程序,它的执行具有原子性。也就是说,这段程序的运行必须一气呵成,不可中断。也就是进程控制(进程转换)是不可中断的
假设PCB中的变量state 表示进程当前所处状态,1表示就绪态,2表示阻塞态。此时进程2等待的事件
发生,则操作系统中,负责进程控制的内核程序至少需要做这样两件事:
①将PCB2的state 设为1
②将PCB2从阻塞队列放到就绪队列
若完成了第一步后收到中断信号,那么PCB2的state=1,但是它却被放在阻塞队列里,如下图所示:
所以如果不能“一气呵成”地执行完进程,就有可能导致操作系统中的某些关键数据结构信息不统一的情况,这会影响操作系统进行别的管理工作。所以需要用“原语”来实现。
如何实现原语的“原子性”
原语可以用“关中断指令”和“开中断指令”这两个特权指令实现原子性。
注:普通的用户程序不能使用这两个指令,所以其为特权指令,只能运行在操作系统的内核中。
在正常情况下,CPU每执行完一条指令都会例行检查是否有中断信号需要处理,如果有,则暂停运行当前这段程序,转而执行相应的中断处理程序。若CPU执行了关中断指令之后,就不再例行检查中断
信号,直到执行开中断指令之后才会恢复检查。
例如此时指令a后有外部中断信号,CPU不会响应这一中断信号,会继续往下处理,直到CPU执行了开中断指令后,才会恢复检查。这样,关中断、开中断 之间的这些指令序列就是不可被中断的,这就实现了“原子
六.进程控制相关原语
1.创建原语
操作系统创建一个进程时,会使用创建原语。
1.申请空白PCB 2.为新进程分配所需资源 3.初始化PCB 4.将PCB插入就绪队列中
所以创建原语使进程从创建态--->就绪态
引起进程创建的事件:
•用户登录:分时系统中,用户登录成功,系统会建立为其建立一个新的进程
•作业调度:多道批处理系统中,有新的作业放入内存时,会为其建立一个新的进程
•提供服务:用户向操作系统提出某些请求时,会新建一个进程处理该请求
•应用请求:由用户进程主动请求创建一个子进程
2.撤销原语
撤销原语可以使程序从某种状态转为终止态,最终从进程中消失
1.从PCB集合中找到终止进程的PCB
2.若进程正在运行,立即剥夺CPU,将CPU分配给其它进程
3.终止其所有子进程(进程间的关系是树形结构,PID为0,1的进程是祖先进程,这两个进程又创建了其他子进程)
4.将该进程拥有的所有资源归还给父进程或操作系统
5.删除PCB
引起进程终止的事件:
•正常结束:进程自己请求终止(exit系统调用)
•异常结束:整数除以0,非法使用特权指令,然后被操作系统强行杀掉
•外界干预:Ctrl+Alt+delete,用户选择杀掉进程
3.阻塞原语
某个进程从运行状态到阻塞状态,就会用到阻塞原语
1.找到要阻塞的进程对应的PCB
2.保护进程运行现场,将PCB状态信息设置为"阻塞态",暂时停止进程运行
3.将PCB插入相应事件的等待队列
引起进程阻塞的事件:
•需要等待系统分配某种资源
•需要等待相互合作的其他进程完成工作
4.唤醒原语
某个进程从阻塞态到就绪态,就会用到唤醒原语
1.在事件等待队列中找到PCB
2.将PCB从等待队列移除,设置进程为就绪态
3.将PCB插入就绪队列,等待被调度
引起进程唤醒的事件:
•等待的事件发生,一个进程被什么事件阻塞,就应该被这一事件唤醒,所以阻塞原语与唤醒原语是成对存在的
5.切换原语
使一个处在运行态的进程,下处理机,回到就绪队列,并且使一个处在就绪态的进程,上处理机,切
换为运行态。
1.将运行环境信息存入PCB
2.PCB移入相应队列
3.选择另一个进程执行,并更新其PCB
4.根据PCB恢复新进程所需的运行环境
引起进程切换的事件:
•当前进程时间片到
•有更高优先级的进程到达
•当前进程主动阻塞
•当前进程终止
补充:这里的恢复运行环境是什么意思呢
程序指令会存放在CPU的寄存器中,当指令依次执行,寄存器的数据可能会更新覆盖,当操作系统想要使另外一个进程上CPU时,另一个进程也要使用到这些寄存器,此时,可以将这些寄存器中必要的内容保存到PCB中,然后切换为别的进程。当之前的进程想要重新上CPU时,操作系统就可以根据PCB保留的信息,恢复运行环境,接着执行该程序没有执行的指令
所以:运行环境或者说是进程上下文,就是进程运行中,进程产生的中间结果,当进程需要下CPU时,需要将运行环境存储到PCB中,当进程需要上CPU时,可以根据PCB恢复进程所需的运行环境。
七.进程间的通信
进程间通信(Inter-Process Communication, IPC)是指两个进程之间产生数据交互。
为什么进程通信需要操作系统支持:
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立,由于进程不能直接访问其他进程的空间,所以若两个进程需要进行进程通信,就需要通过操作系统进行进程间的通信。
进程通信有三种方式:
1.共享存储
某个进程可以申请一片共享存储区,这片共享存储区可以被其他进程共享,所以P想要向Q传递数据,就可以先写到共享存储区,然后由Q将共享内存区映射到自己的地址空间中。
共享存储是可以是基于存储区的共享,即操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式。
也可以是基于数据结构的共享,比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式。
注:通过“增加页表项/段表项”即可将同一片共享内存区映射到各个进程的地址空间中
为避免出错,各个进程对共享空间的访问应该是互斥的。各个进程可使用操作系统内核提供的同步斥工具(如P、V操作)
2.消息传递
进程间的数据交换以格式化的消息”(Message)为单位。进程通过操作系统提供的“发送消息/接收消息,两个原语进行数据交换。
格式化的消息由两个部分组成:消息头,消息体
消息头:发送进程ID、接受进程ID、消息长度等格式化的信息
消息体:一个进程要传递给另一个进程的数据
1)直接通信方式
消息发送进程要指明接收进程的ID
直接通信的过程如下:
1.进程P在自己的地址空间中,完善需要发送给Q的消息,并且使用发送原语send(Q,msg),来指明这个消息是要发送给Q进程
2.接下来操作系统内核会接收到这个消息,并且将其挂在进程Q的消息队列中,也就是进程P的用户空间被复制到了内核空间中
3.进程Q会使用接收原语receive(P,&msg),操作系统会检查进程Q的消息,看其中是否有Q想要接收的消息,若有,操作系统内核会将这个消息复制到进程Q的地址空间中。
2)间接通信方式
通过“信箱”间接地通信,因此又称“信箱通信方式
间接通信过程如下:
1.若进程P与进程Q需要进行通信,那么进程P可以通过系统调用申请一个信箱或多个信箱
2.接着在进程P在自己的地址空间中完善消息的内容
3.用发送原语send(A,msg)来指明我要发送到A信箱(这里是发送到A信箱,并没有指明是哪一个进程)
4.进程Q使用接收原语receive(A,&msg)从信箱A接受消息,它可以指明从哪个进程中接收,这样信箱A的msg就会被复制到进程Q的地址空间中了。
注:可以多个进程往同一个信箱send消息,也可以多个进程从同一个信箱中receive消息
3.管道通信
管道的意思即从管道的一边写进程,从管道的另一边读进程,数据的流动只能是单向的
“管道”是一个特殊的共享文件,又名pipe文件。其实就是在内存中开辟一个大小固定的内存缓冲区。
若两个进程需要用管道通信,进程P通过系统调用的方式向系统申请一个管道文件,操作系统会新建一个管道文件,实质上就是开辟一个大小固定的内存缓冲区。并且从管道中写数据或读数据,并且数据是先进先出(FIFO)的。
注:固定大小的缓冲区本质是一个循环队列。
1.由于只能单向,所以管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。
2.各进程要互斥地访问管道(由操作系统实现)
3.当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。
4.当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
5.管道中的数据一旦被读出,就彻底消失。因此,当多个进程读同一个管道时,可能会错乱。对此,
通常有两种解决方案:
①一个管道允许多个写进程,一个读进程。
②允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据 (Linux 的方案)。
管道通信与共享存储的区别在于:
对于共享存储,进程P,Q都可以从共享区域中读写数据,没有限制
但是对于管道通信而言,首先数据的流动是单向的,例如,P写数据,Q读数据,或者反过来。其次数据是先进先出的,也就是Q不是任意读取管道的数据,而是P先写进来的数据,Q才能读