学习日期:2024.6.30
内容摘要:进程控制的概念,进程控制相关的“原语”,进程通信
进程控制
原语
进程控制用“原语”实现。原语是一种特殊的程序,它的执行具有原子性,也就是说,这段程序的执行必须一气呵成,不能中断。
Q:为什么进程控制(状态转换)的过程必须一气呵成?
如果不能一气呵成,可能会出现某些进程的关键数据结构信息不统一的情况,会影响操作系统进行别的管理工作。
Q:原子性是如何实现的?
利用了“关中断指令”和“开中断指令”两个特权指令来实现原子性,CPU执行了关中断指令后,就不会再例行检查中断信号,直到执行开中断指令之后才会恢复检查,从而达到独占CPU的目的。
进程的创建
创建原语(操作系统创建一个进程时使用的原语)会申请空白PCB,为新进程分配所需资源,初始化PCB,将PCB插入就绪队列(创建态->就绪态)
引起进程创建的事件:
①用户登录:分时系统中,用户登陆成功,系统会为其建立一个新的进程。
②作业调度:多道批处理系统中,有新的作业放入内存时,会为其建立新进程。
③提供服务:用户向操作系统提出请求时,会新建一个进程处理请求。
④应用请求:由用户进程主动请求创建一个子进程。
进程的终止
撤销原语(操作系统终止一个进程时所使用的原语)会从PCB集合中找到终止进程的PCB,若进程正在运行,立刻剥夺CPU,同时将CPU分配给其它进程。终止其所有子进程,将该进程所拥有的所有资源归还给父进程或操作系统,最后删除PCB
引起进程终止的事件:
①正常结束:进程自己请求终止。
②异常结束:整数除以0,非法使用特权指令被操作系统强行杀死。
③外界干预:出于某种原因,用户使用任务管理器等方式杀死进程。
进程的阻塞
阻塞原语可以让进程从运行态变为阻塞态。阻塞原语会找到要阻塞的进程对应的PCB,保护进程运行现场,将PCB状态信息设置为“阻塞态”,暂时停止进程运行,最后将PCB插入相应事件的等待队列。
引起进程阻塞的事件:
①等待:需要等待系统分配某种资源,或需要等待互相合作的其它进程完成工作
进程的唤醒
唤醒原语可以让进程从就绪他转换为运行态,唤醒原语会在等待队列中找到待唤醒进程的PCB,将PCB从等待队列移除,设置进程为就绪态,将PCB插入就绪队列,等待被调度
引起进程唤醒的事件:
①进程因何事被阻塞,就应当由何事被唤醒,阻塞原语和唤醒原语必须是成对使用的。
进程的切换
切换原语会让进程在运行态和就绪态之间切换。切换原语会将进程的运行环境信息存入PCB,然后将PCB移入相应队列(就绪/运行),选择另一个进程执行,并更新其PCB,根据PCB恢复新进程所需的运行环境。
引起进程切换的事件:
①当前进程的时间片到。 ②有更高优先级的进程到达。③当前进程主动阻塞。④当前进程终止。
共性
无论是哪个进程控制原语,要做的无非是三类事情:
①更新PCB中的信息(修改进程状态,保护/恢复运行环境)。
②将PCB插入合适的队列。
③分配/回收资源。
什么叫“进程的运行环境”?
我们在前面提到过很多次“保存运行环境”,其实就是保存寄存器中的指令和值,在进程切换时,先在PCB中保存这个进程的运行环境(保存如PC,IR,通用寄存器等信息)然后当原来的进程再次运行时,可以通过PCB快速的恢复它的运行环境。
进程通信
进程间通信(IPC,Inter-Process Communication)是指两个进程之间产生数据交互。
你在刷b站的过程中看到一个有意思的视频,想把它分享给你的好朋友,会点击视频下面的转发按钮,然后选择你的微信好友,视频就发出去了,但是正在运行的bilibili和微信显然不是同一个进程,那是怎么发出去的呢?这就是进程之间发生了通信。
为什么进程的通信需要操作系统支持?
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立。
如果两个进程P和Q之间需要通信,显然进程P是不能直接把数据写到Q的地址空间的,所以需要借助操作系统的帮助。下面介绍共享存储、消息传输、管道通信三种进程通信方式。
共享存储
共享存储的原理是,进程可以申请一片共享存储区,这片共享存储区其它的进程也可以访问,所有要进行交换的数据就可以放在这片区域,从而完成进程间通信。通过增加页表项/段表项即可将同一片共享内存区域映射到各个进程的地址空间当中。
为避免写覆盖,各个进程对共享空间的访问应该是互斥的。(避免P正在该区域写,此时Q也在同一区域写,覆盖了P写的内容)
基于存储区的共享
操作系统在内存中划出一块共享存储区域,数据的形式、存放的位置都由通信进程控制,而不是操作系统,这种共享方式速度很快,是一种高级通信方式。
基于数据结构的共享
比如说共享空间内只能放一个长度为100的数组,这个数组能在各个进程之间共享访问,这种共享方式速度慢限制多,是一种低级通信方式。
消息传递
进程间的数据交换以格式化的消息(Message)为单位,进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。
直接通信方式
在操作系统的内核区域中,进程Q的PCB中会有一个进程Q的消息队列。进程P给进程Q发送消息时,进程P在自己的区域中初始化消息,然后使用发送原语,send(Q,msg),这会使得操作系统接收到了这个消息,把这个消息复制到进程Q的消息队列当中。进程Q在运行时,可以使用接收原语,receive(P,&msg),操作系统查找Q的消息队列,发现P发来的信息,再把消息队列当中的msg放到进程Q的地址空间当中。
间接通信方式
进程可以向操作系统申请一个(或多个)邮箱,保存在操作系统内核地址空间当中。当进程P准备给Q发送信息时,进程P首先在自己的地址空间内给自己要发送的消息赋值(好比先写好信),然后使用发送原语(A,msg)往信箱A发送消息msg。注意!此时与直接通信方式的区别是,不声明自己消息要发给谁,而是发送给信箱,以信箱作为中间实体进行传递。
之后,进程Q通过接收原语,receive(A,&msg)从信箱A接受消息。
同现实中的信箱一样,多个进程可以在同一个信箱send或receive消息。
管道通信
水管当中的水流一定是单向的,管道通信也是如此,我们只能从一端写数据,一端读数据。
“管道”(pipe)是一个特殊的共享文件,其实就是在内存中开辟一个大小固定的内存缓冲区,其与共享存储的根本区别是:共享内存的P、Q进程都可以在共享存储区当中自由读写数据,但管道是单向的,P只能向管道里写数据,Q只能读数据,且管道中的数据满足先进先出的特征,Q不是想读哪个就读哪个,而是按照P发送的顺序来读。
①管道只能采用半双工通信,某一时间段内只能实现单向的传输(管道可以倒过来,但是不能同时来回传)。如果需要双向同时通信(全双工通信),则需要建立两个管道。
②各个进程要互斥的访问管道(由操作系统实现)
③因为管道的大小是固定的,当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。同理,当管道读空时,读进程将堵塞,直到写进程往管道中写入数据。
④管道中的数据一旦被读出,就彻底消失,因此当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:
1.一个管道允许多个写进程,但是只能有一个读进程(多个进水口,一个出水口)
2.允许有多个写进程和多个读进程,但是系统会让各个读进程轮流读数据(多个出水口,但不准同时打开)(Linux的方案)
内容总结自王道计算机考研《操作系统》 和 人民邮电出版社《操作系统导论》