第二章 进程的描述与控制
2.1 前趋图和程序执行
2.1.1 前趋图
存在意义:为了能更好地描述程序的顺序和并发执行情况;
前趋图:是一个有向无循环图,可记为DAG,它用于描述进程之间执行的先后顺序。
注:进程(或程序)之间的前趋关系可用 “ → ” 表示,如果进程Pi和Pj存在着前趋关系,可表示为(Pi,Pj)∈→,也可写成Pi→Pj,表示在Pj执行之前Pi必须完成。
Pi是Pj的直接前趋,而称Pj是Pi的直接后继;
具有循环的前趋图:
具有九个节点的前趋图:
2.1.2 程序顺序执行
-
程序的顺序执行
例:
S1: a :=x+y;
S2: b :=a-5;
S3: c :=b+1;
很明显三者的前趋关系为:S1→S2→ S3
-
程序顺序执行时的特征
① 顺序性:指处理机严格地按照程序所规定的顺序执行,即每一操作必须在下一个操作开始之前结束;
② 封闭性:指程序在封闭的环境下运行,即程序运行时独占全机资源,资源的状态(除初始状态外)只有本程序才能改变它,程序一旦开始执行,其执行结果不受外界因素影响;
③ 可再现性:指只要程序执行时的环境和初始条件相同,当程序重复执行时,不论它是从头到尾不停顿地执行,还是“停停走走”地执行,都可获得相同的结果。程序顺序执行时的这种特性,为程序员检测和校正程序的错误带来了很大的方便。
2.1.3 程序并发执行
-
程序的并发执行
I 代表输入操作,C代表计算操作,P为打印操作
例:
S1: a :=x+2;
S2: b :=y+4;
S3: c :=a+b;
S4: d :=c+b;
由于S1和S2彼此不相互依赖,故可以并发执行;
四条语句的前趋关系:
程序并发执行时的前趋图:
-
程序并发执行时的特征
(1)间断性
产生原因:
程序在并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使在这些并发执行的程序之间形成了相互制约的关系;
相互制约将导致并发程序具有:“执行—暂停—执行”这种间断性的活动规律。
例:C和Р是三个相互合作的程序,当计算程序完成 Ci-1的计算后,如果输入程序Ⅰi尚未完成数据的输入,则计算程序 Ci就无法进行数据处理,必须暂停运行。只有当使程序暂停的因素消失后(如Ⅰi已完成数据输入),计算程序 Ci;便可恢复执行。由此可见,相互制约将导致;
(2)失去封闭性
产生原因:
当系统中存在着多个可以并发执行的程序时,系统中的各种资源将为它们所共享,而这些资源的状态也由这些程序来改变,致使其中任一程序在运行时,其环境都必然会受到其它程序的影响;
例:
当处理机被分配给某个进程运行时,其他程序必须等待。显然,程序的运行已失去了封闭性。
(3)不可再现性
产生原因:
程序在并发执行时,由于失去了封闭性,也将导致其又失去可再现性。
程序在并发执行时,由于失去了封闭性,其计算结果必将与并发程序的执行速度有关,从而使程序的执行失去了可再现性。换而言之,程序经过多次执行后,虽然它们执行时的环境和初始条件相同,但得到的结果却各不相同。
2.2 进程的描述
2.2.1 进程的定义和特征
-
进程的定义
为了使参与并发执行的每个程序(含数据)都能独立地运行,在操作系统中必须为之配置一个专门的数据结构,称为进程控制块(Process Control Block,PCB)。系统利用PCB来描述进程的基本情况和活动过程,进而控制和管理进程。这样,由程序段、相关的数据段和PCB三部分便构成了进程实体(又称进程映像)。一般情况下,我们把进程实体就简称为进程;
(1)进程是程序的一次执行;
(2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动;
(3)进程是具有独立功能 的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位;
在引入了进程实体的概念后,我们可以把传统OS中的进程定义为: “进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。”
-
进程的特征
(1)动态性
(2)并发性
(3)独立性
(4)异步性
2.2.2 进程的基本状态及转换
-
进程的三种基本状态
(1)就绪(Ready)状态
(2)执行(Running)状态
(3)阻塞(Block)状态
-
三种基本状态的转换
-
创建状态和终止状态
存在目的:
为了满足进程控制块对数据及操作的完整性要求以及增强管理的灵活性。
(1)创建状态
(2)终止状态
因而有进程的五种基本状态及转换:
2.2.3 挂起操作和进程状态的转换
-
挂起操作的进入
(1)终端用户的需要
(2)父进程请求
(3)负荷调节的需要
(4)操作系统的需要
-
引入挂起原语操作后三个进程状态的转换
挂起原语:Suspend 激活原语:Active
(1)活动就绪→静止就绪
(2)活动阻塞→静止阻塞
(3)静止阻塞→活动就绪
(4)静止阻塞→活动阻塞
具有挂起状态的进程状态图:
-
引入挂起操作后五个缉拿换成状态的转换
(1)NULL→创建
(2)创建→活动就绪
(3)创建→静止就绪
(4)执行→终止
具有创建、终止和挂起状态的进程状态图:
2.2.4 进程管理中的数据结构
-
操作系统中用于管理控制的数据结构
OS管理的这些数据结构一般分为以下四类:内存表、设别表、文件表和用于进程管理的进程表;
-
进程控制块PCB的作用
为了便于描述和管理进程的运行,在OS的核心为每个进程专门定义了一个数据结构—进程控制块PCB
PCB的作用:
(1)作为独立运行基本单位的标志
(2)能实现间断性运行方式
(3)提供进程管理所需要的信息
(4)提供进程调度所需要的信息
(5)实现与其他进程的同步与通信
-
进程控制块中的信息
(1)进程标识符
-
外部标识符:通常由字母、数字组成等。
-
内部标识符:通常是一个进程的序号(每一个进程一个唯一的数字标识符)
(2)处理机状态
- 通用寄存器:是用户程序可以访问的,用于暂存信息
- 指令计数器:纯法要访问的下一条指令的地址
- 程序状态字PSW:含有状态信息,如条件吗、执行方式、中断屏蔽标志等
- 用户栈指针:用于存放过程和系统调用参数及调用地址。
(3)进程调度信息
- 程序和数据的地址
- 进程同步和通信机制
- 资源清单
- 链接指针
-
-
进程控制块的组织方式
(1)线性方式
定义:将系统中所有的PCB都组织在一张线性表中,将表的首址存放在内存的一个专用区域中。
优点:实现简单,开销小;
缺点:每次查找都需要扫描整张表,因此适合进程数目不多的系统
示意图:
(2)链接方式
定义:把具有相同状态进程的PCB分别通过PCB中的链接字链接成功一个队列;这样就形成就绪队列、若干个阻塞队列和空白队列,且就绪队列按进程的优先级将PCB从高到低进行排列,高的在前。
示意图:
(3)索引方式
定义:系统根据所有进程状态的不同,建立几张索引表。
示意图:
2.3 进程控制
2.3.1 操作系统内核
通常将一些与硬件紧密相关的模块(如中断处理程序等)、各种常用设备的驱动程序以及运行频率较高的模块(如时钟管理、进程调度和许多模块所公用的一些基本操作),都安排在紧靠硬件的软件层次中,将它们常驻内存,即通常被称为的OS内核。这种安排方式的目的在于两方面:
① 便于对这些软件进行保护,防止遭受其他应用程序的破坏;
② 是可以提高OS的运行效率。
(1)系统态:别称管态
也称为内核态。具有较高的特权,能执行一切指令,访问所有寄存器和存储区和存储区,传统的OS都在系统态运行.
(2)用户态:别称目态
都在系统态运行。②用户态:又称为目态。它是具有较低特权的执行状态,仅能执行规定的指令,访问指定的寄存器和存储区。一般情况下,应用程序只能在用户态运行,不能去执行OS指令及访问OS区域,这样可以防止应用程序对OS的破坏。
-
支撑功能
(1)中断处理
是内核最基本的功能,是整个操作系统赖以活动的基础
(2)时钟管理
是内核的一项基本功能;
(3)原语操作
由若干条指令组成的,用于完成一定功能的一个过程。原子操作是一个不可分割的基本单位。
-
资源管理功能
(1)进程管理
一种是由于各个功能模块的运行频率较高:进程的调度与分派、进程的创建于撤销;
二是由于他们为多种功能模块所需要,:用于实现进程同步的原语、常用的进程通信原语等。
通常都将上述进程放在内核中,以提高OS的性能。
(2)存储器管理
通常被放在内核中,以保证存储器管理具有较高的运行速度
(3)设备管理
设备管理与硬件紧密相关,因此其中很大部分也都设置在内核中
2.3.2 进程的创建
-
进程的层次结构
正确理解父进程和子进程相关概念;
-
进程图
为了形象地描述一个进程的家族关系而引入进程图。类似于数据结构的“树”
进程树:
-
引起创建进程的时间
(1)用户登录
(2)作业调度
(3)提供服务
(4)应用请求
-
进程的创建
(1)申请空白PCB
(2)为新进程分配其运行所需的资源
- 对于批处理作业,其大小可在用户提出创建进程要求时提供;
- 若是为应用进程创建子进程,也是在该进程提出创建进程的请求中给出所需内存的大小;
- 对于交互型作业,用户可以不给出内存要求二由西永分配一定的空间;
(3)初始化进程控制块PCB
- 初始化表示信息
将系统分配的标识符和父进程标识符填入新PCB中;
- 初始化处理机状态信息
使程序计数器指向程序的入口地址,使栈指针指向栈顶;
- 初始化处理机控制信息
将进程的状态设置为就绪状态或静止就绪状态;
(4)如果进程就绪队列能够接纳新进程,便将新进程插入就绪队列
2.3.3 进程的终止
-
引起进程终止的事件
(1)正常结束
表示进程的任务已经完成,准备退出运行;
(2)异常结束
指进程在运行时发生了某种异常事件,使该程序无法继续运行;
- 越界错
- 保护错
- 非法指令
- 特权指令错
- 运行超时
- 等待超时
- 算术运算错
- I/O故障
(3)外界干预
指进程应外界的请求而终止运行;
- 操作员或操作系统干预
指如果系统中发生了某事件,例如,发生了系统死锁,由操作员或操作系统采取终止某些进程的方式使系统从死锁状态中解救出来;
- 父进程请求
指当子进程已完成父进程所要求的任务时,父进程可以提出请求结束该子进程;
- 因父进程终止
指当父进程终止时,它的所有子进程也都应当结束,因此,OS在终止父进程的同时,也将它的所有子孙进程终止。
-
进程的终止过程
(1)根据被终止进程的标识符,从PCB集合中检索出该进程的OCB,从中读出该进程的状态;
(2)若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真,用于指示该进程被终止后应重新进行调度;
(3)若该进程还有子孙进程,还应将其所有子孙进程也都予以终止,以防他们成为不可控的进程;
(4)将被终止进程所拥有的全部资源规划给其父进程,或归还给系统;
(5)将被终止进程(PCB)从所在队列(或链表)中移出,等待其他程序来收集信息。
2.3.4 进程的阻塞与唤醒
-
引起进程阻塞和唤醒的事件
当出现下述几类事件会引起进程阻塞或被唤醒:
(1)向系统请求共享资源失败
(2)等待某种操作的完成
(3)新数据尚未到达
(4)等待新任务的到达
-
进程阻塞过程
正在执行的进程,如果发生了上述某事件,进程便通过调用阻塞原语Block将自家阻塞;可见,阻塞是进程自身的一种主动行为。
-
进程唤醒过程
当被阻塞进程说期待的事件发生时,比如它所启动的I/O操作已完成,或所期待的数据已经到达,则有关进程调用唤醒原语Wakeup。
执行过程:首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后再将该PCB插入到就绪队列中。
注意:Block原语和Wakeup原语是成对出现的
安排Block原语的同时应在与之合作、或其他相关的进程中安排一条相应的Wakeup原语,否则阻塞进程因不能被唤醒,而永久地处于阻塞状态,再无机会继续运行;
2.3.5 进程的挂起和激活
-
进程的挂起
挂起原语Suspend
执行过程:
首先检查被挂起进程的状态,若处于活动就绪状态,便将其改为静止就绪;对于活动阻塞状态的进程,则将之改为静止阻塞。
-
进程的激活
激活原语Active
执行过程:
检查该进程的现行状态,若是静止就绪,便将之改为活动就绪;若为静止阻塞,便将之改为活动阻塞。
2.4 进程同步
2.4.1 进程同步的基本概念
-
两种形式的制约关系
(1)简介相互制约关系
例:
打印机、磁带机等临界资源;
(2)直接相互制约关系
例:
输入进程A和计算进程B的通力合作
-
临界资源
生产者消费者问题:
生产者:
消费者:
-
临界区
进程中除上述进入区、临界区及退出区之外的其他部分的代码在这里都称为剩余区;
访问临界资源的循环进程:
while(TRUE)
{
进入区
临界区
退出区
剩余区
}
-
同步机制应遵循的规则
(1)空闲让进
(2)忙则等待
(3)有限等待
(4)让权等待
2.4.2 硬件同步机制
-
关中断
关中断是实现互斥的最简单的方法之一
关中断缺点:
① 滥用关中断权力可能导致严重后果;
② 关中断时间过长,会影响系统效率,限制了处理器交叉执行程序的能力;
③ 关中断方法也不适用于多CPU系统,因为在一个处理器上关中断并不能防止进程在其它处理器上执行相同的临界段代码。
-
利用Test-and-Set指令实现互斥
TS指令的一般性描述如下:
我们可以看出TS指令也是一条原语;
lock的状态存在两种:
- lock = FALSE,表示该资源空闲
- lock = TRUE,表示该资源正在被使用
利用TS指令实现互斥的循环进程结构:
-
利用Swap指令实现进程互斥
Swap指令:
利用Swap指令实现互斥的循环进程结构:
2.4.3 信号量机制
-
整型信号量
wait(S):
signal(S):
-
记录型信号量
在信号量机制中,除了需要一个用于代表资源数目的整型变量value外,还应增加一个进程链表指针list,用于链接上述的所有等待进程;
指令集:
相应地,wait(S)和signal(S)操作可描述为:
-
AND型信号量
AND同步机制的基本思想:
将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配采取原子操作方式;
Swait(Simultaneous wait)指令:
-
信号量集
存在意义:提高效率,减少死锁的概率;
Swait信号集格式:
Swait(S1, t1, d1,…,Sn, tn, dn);
Ssignal信号集格式:
Ssignal(S1, d1,…,Sn, dn);
特殊情况:
(1)Swait(S, d, d)。此时在信号量集中只有一个信号量S,但允许它每次申请d个资源,当现有资源数少于d时,不予分配。
(2)Swait(S, 1, 1)。此时的信号量集已蜕化为一般的记录型信号量(S > 1时)或互斥信号量(S = 1时)。
(3)Swait(S, 1, 0)。这是一种很特殊且很有用的信号量操作。当S ≥ 1时,允许多个进程进入某特定区;当S变为0后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。
2.4.4 信号量的应用
-
利用信号量实现进程互斥
(1)设mutex为互斥信号量,其初值为1,取值范围为(-1,0,1)。当mutex = 1时,表示两个进程皆未进入需要互斥的临界区;当mutex = 0时,表示有一个进程进入临界区运行,另外一个必须等待,挂入阻塞队列;当mutex = -1时,表示有一个进程正在临界区运行,另外一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒;
(2)代码描述:
-
利用信号量实现前趋关系
前趋图:
代码框架:
2.45 管程机制
-
管程的定义
代表共享资源的数据结构以及由对该共享数据结构实施操作的一组过程组成的资源管理程序共同构成了一个操作系统的资源管理模块,我们称之为管程;
管程由四部分组成:
① 管程的名称;
② 局部与管程的共享数据结构说明;
③ 对该数据结构进行操作的一组过程;
④ 对局部于管程的共享数据设置初始值的语句;
管程的示意图:
管程的语法描述:
注:封装于管程内部的数据结构仅能被封装于管程内部的过程访问;
管程特性:
① 模块化:即管程是一个基本程序单位,可以单独编译;
② 抽象数据类型:抽象数据类型,指管程中不仅有数据,而且有对数据的操作;
③ 信息掩蔽:指管程中的数据结构只能被管程中的过程访问,这些过程也是在管程内部定义的,供管程外的进程调用,而管程中的数据结构以及过程(函数)的具体实现外部不可见
管程和进程的去呗:
① 虽然二者都定义了数据结构,但进程定义的是私有数据结构PCB,管程定义的是公共数据结构,如消息队列等;
② 二者都存在对各自数据结构上的操作,但进程是由顺序程序执行有关操作,而管程主要是进行同步操作和初始化操作;
③ 设置进程的目的在于实现系统的并发性,而管程的设置则是解决共享资源的互斥使用问题;
④ 进程通过调用管程中的过程对共享数据结构实行操作,该过程就如通常的子程序一样被调用,因而管程为被动工作方式,进程则为主动工作方式;
⑤ 进程之间能并发执行,而管程则不能与其调用者并发;
⑥ 进程具有动态性,由“创建”而诞生,由“撤消”而消亡,而管程则是操作系统中的一个资源管理模块,供进程调用。
-
条件变量
管程中对每个条件变量都须予以说明,其形式为: condition x, y;对条件变量的操作仅是wait和 signal,因此条件变量也是一种抽象数据类型,每个条件变量保存了一个链表,用于记录因该条件变量而阻塞的所有进程,同时提供的两个操作即可表示为x.wait和x.signal,其含义为:
① x.wait:正在调用管程的进程因x条件需要被阻塞或挂起,则调用x.wait 将自己插入到x条件的等待队列上,并释放管程,直到x条件变化。此时其它进程可以使用该管程。
② x.signal:正在调用管程的进程发现x条件发生了变化,则调用x.signal,重新启动一个因x条件而阻塞或挂起的进程,如果存在多个这样的进程,则选择其中的一个,如果没有,继续执行原进程,而不产生任何结果。这与信号量机制中的signal操作不同。因为,
后者总是要执行s : = s + 1操作,因而总会改变信号量的状态。如果有进程Q因x条件处于阻塞状态,当正在调用管程的进程P执行了x.signal操作后,进程Q被重新启动,此时两个进程Р和Q,如何确定哪个执行哪个等待,可采用下述
两种方式之一进行处理:
(1)Р等待,直至Q离开管程或等待另一条件。
(2)Q等待,直至Р离开管程或等待另一条件。Hoare采用了第一种处理方式;
Hansan选择了两者的折中,他规定管程中的过程所执行的signal操作是过程体的最后一个操作,于是,进程Р执行signal操作后立即退出管程,因而,进程Q马上被恢复执行。
2.5 经典进程的同步问题
2.5.1 生产者-消费者问题
-
利用记录型信号量解决生产者-消费者问题
假定在生产者和消费者之间的公用缓冲池中具有n个缓冲区,
互斥信号量:mutex
作用:实现诸进程对缓冲池的互斥使用;
信号量:empty 表示缓冲池中
信号量:full 表示空缓冲
且生产者消费者相互等效;
注:wait(mutex)和signal()
语言解释:
-
利用AND信号量解决生产者-消费者问题
可利用AND信号量来解决;
即用Swait(empty,mutex)来代替wait(empty)和 wait(mutex);
用Ssignal(mutex, full)来代替signal(mutex)和 signal(full);
用Swait(full,mutex)代替wait(full)和wait(mutex);
用Ssignal(mutex,empty)代替Signal(mutex)和Signal(empty);
利用AND信号量来解决生产者-消费者问题的算法中的生产者和消费者可描述如下:
-
利用管程解决生产者-消费者问题
利用管程方法解决问题时,首先便是为他们建立一个管程 并命名procducerconsumer,或简称PC,内含两个过程;
(1)put(x),生产过程
(2)get(x),消费过程
对于变量notfull和notempty,分别有 两个过程cwait和csignal对他们进行操作;
cwait(condition)过程:当管程被一个进程占用时,其他进程调用该过程时阻塞;并挂在条件condition的队列上。
csignal(condition)过程:唤醒在cwait执行后阻塞在条件condition队列上的进程,如如果这样的进程不止一个,则选择其中一个实施唤醒操作;如果队列为空,则无操作而返回。
PC管程可描述如下:
在利用管程解决生产者-消费者问题时,其中的生产者和消费者可描述为:
2.5.2 哲学家进餐问题
-
利用记录型信号量解决哲学家进餐问题
经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用。为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量构成信号量数组。其描述如下:
semaphore chopstick[5]={1,1,1,1,1};
所有信号量均被初始化为1,第i位哲学家的活动可描述为:
解决办法:
(1)至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两只筷子,从而使更多的哲学家能够进餐。
(2)仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐。
(3)(规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。按此规定,将是1、2号哲学家竞争1号筷子;3、4号哲学家竞争3号筷子。即五位哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获得两只筷子而进餐。
-
利用AND信号机制解决哲学家进餐问题
在哲学家进餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐,这在本质上就是前面所介绍的AND同步问题,故用AND信号量机制可获得最简洁的解法。
语言描述:
2.5.3 读者-写者问题
-
利用记录型信号量解决读者-写者问题
为实现Reader 与 Writer进程间在读或写时的互斥而设置了一个互斥信号量Wmutex。另外,再设置一个整型变量Readcount表示正在读的进程数目。由于只要有一个Reader进程在读,便不允许Writer进程去写。因此,仅当Readcount=O,表示尚无Reader进程在读时,Reader进程才需要执行Wait(Wmutex)操作。若wait(Wmutex)操作成功,Reader进程便可去读,相应地,做Readcount+1操作。同理,仅当Reader进程在执行了Readcount减1操作后其值为О时,才须执行signal(Wmutex)操作,以便让Writer进程写操作。又因为Readcount是一个可被多个Reader进程访问的临界资源,因此,也应该为它设置一个互斥信号量rmutex。
读者-写者问题可描述如下:
-
利用AND信号机制解决读者-写者问题
这里的读者一写者问题,与前面的略有不同,它增加了一个限制,即最多只允许RN个读者同时读。为此,又引入了一个信号量L,并赋予其初值为RN,通过执行wait(L,1,1)操作来控制读者的数目,每当有一个读者进入时,就要先执行wait(L,1,1)操作,使L的值减1。当有RN个读者进入读后,L便减为0,第 RN+1个读者要进入读时,必然会因wait(L,1,1)操作失败而阻塞。
对利用信号量集来解决读者-写者问题的描述如下:
其中,Swait(mx, 1, 0)语句起着开关的作用。只要无writer进程进入写操作,mx = 1,其中,Swait(mx, 1, 0)语句起着开关的作用。只要无writer进程进入写操作,mx = 1,reader进程就都可以进入读操作。但只要一旦有writer进程进入写操作时,其mx =0,则任何reader进程就都无法进入读操作。Swait(mx, 1, 1, L, RN, O)语句表示仅当既无writer进程在写操作(mx =1)、又无reader进程在读操作(L = RN)时,writer进程才能进入临界区进行写操作。
2.6 进程通信
低级进程通信:
① 效率低,生产者每次只能向缓冲池投放一个产品(消息),消费者每次只能从缓冲区取得一个消息;
② 通信对用户不透明,OS只为进程之间的通信提供了共享存储器。
高级通信工具:
① 使用方便。OS隐藏了实现进程通信的具体细节,向用户提供了一组用于实现高级通信的命令(原语),用户可方便地直接利用它实现进程之间的通信。或者说,通信过程对用户是透明的。这样就大大减少了通信程序编制上的复杂性。
② 高效地传送大量数据。用户可直接利用高级通信命令(原语)高效地传送大量的数据。
2.6.1 进程通信的类型
-
共享存储器系统
共享存储器系统中,相互通信的进程共享某些数据结构或共享存储区,进程能够通过这些空间进行通信;
(1)基于共享数据结构的通信方式:
在这种通信方式中,要求诸进程公用某些数据结构,借以实现诸进程间的信息交换,如在生产者-消费者问题中的有界缓冲区。操作系统仅提供共享存储器,由程序员负责对公用数据结构的设置及对进程间同步的处理。这种通信方式仅适于传递相对少量的数据,通信效率低下,属于低级通信。
(2)基于共享存储区的通信方式:
为了传输大量数据,在内存中划出了一块共享存储区域,诸进程可通过对该共享区的读或写交换信息,实现通信,数据的形式和位置甚至访问控制都是由进程负责,而不是OS。这种通信方式属于高级通信。需要通信的进程在通信前,先向系统申请获得共享存储区中的一个分区,并将其附加到自己的地址空间中,便可对其中的数据进行正常读、写,读写完成或不再需要时,将其归还给共享存储区。
-
管道通信系统
定义:
所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名 pipe文件。向管道(共享文件)提供输入的发送进程(即写进程)以字符流形式将大量的数据送入管道;而接受管道输出的接收进程(即读进程)则从管道中接收(读)数据。由于发送进程和接收进程是利用管道进行通信的,故又称为管道通信。
为了协调双方的通信,管道机制必须提供以下三方面的协调能力:
① 互斥,即当一个进程正在对pipe执行读/写操作时,其它(另一)进程必须等待。
② 同步,指当写(输入)进程把一定数量(如4KB)的数据写入pipe,便去睡眠等待,直到读(输出)进程取走数据后再把它唤醒。当读进程读一空pipe时,也应睡眠等待,直至写进程将数据写入管道后才将之唤醒。
③ 确定对方是否存在,只有确定了对方已存在时才能进行通信。
-
消息传递系统
本方式隐藏了通信实现细节,使通信过程对用户透明化,降低了通行程序设计的复杂性和错误率,成为当前应用最为广泛的一类进程间通信的机制;
(1)直接通信方式
是指发送过程利用OS所提供的发送原语,直接把消息发送给目标进程;
(2)简介通信方式
是指发送和接受进程,都通过共享中间实体(称为邮箱)的方式进行消息的发送和接受,完成进程间的通信;
-
客户机-服务器系统
(1)套接字
一个套接字就是一个通信标识类型的数据结构,包含了通信目的的地址、通信使用的端口号、通信网络的传输层协议、进程所在的网络地址,以及针对客户或服务器程序提供的不同系统调用(或API函数)等,是进程通信和网络通信的基本构件。套接字是为客户/服务器模型而设计的,通常,套接字包括两类:
(1)基于文件型:
通信进程都运行在同一台机器的环境中,套接字是基于本地文件系统支持的,一个套接字关联到一个特殊的文件,通信双方通过对这个特殊文件的读写实现通信,其原理类似于前面所讲到的管道;
(2)基于网络型:
该类型通常采用的是非对称方式通信,即发送者需要提供接收者命名。通信双方的进程运行在不同主机的网络环境下,被分配了一对套接字,一个属于接收进程(或服务器端),一个属于发送进程(或客户端)。
优点:它不仅适用于同一台计算机内部的进程通信,也适用于网络环境中不同计算机间的进程通信。
(2)远程过程调用和远程方法调用
远程过程(函数)调用RPC(Remote Procedure Call),是一个通信协议,用于通过网络连接的系统。该协议允许运行于一台主机(本地)系统上的进程调用另一台主机(远程)系统上的进程,而对程序员表现为常规的过程调用,无需额外地为此编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称做远程方法调用。
负责处理远程过程调用的进程有两个,一个是本地客户进程,另一个是远程服务器进程,这两个进程通常也被称为网络守护进程,主要负责在网络间的消息传递,一般情况下,这两个进程都是处于阻塞状态,等待消息。
远程调用的主要步骤是:
(1)本地过程调用者以一般方式调用远程过程在本地关联的客户存根,传递相应的参数,然后将控制权转移给客户存根;
(2)客户存根执行,完成包括过程名和调用参数等信息的消息建立,将控制权转移给本地客户进程;
(3)本地客户进程完成与服务器的消息传递,将消息发送到远程服务器进程;
(4)远程服务器进程接收消息后转入执行,并根据其中的远程过程名找到对应的服务器存根,将消息转给该存根;
(5)该服务器存根接到消息后,由阻塞状态转入执行状态,拆开消息从中取出过程调用的参数,然后以一般方式调用服务器上关联的过程;
(6)在服务器端的远程过程运行完毕后,将结果返回给与之关联的服务器存根;
(7)该服务器存根获得控制权运行,将结果打包为消息,并将控制权转移给远程服务器进程;
(8)远程服务器进程将消息发送回客户端;
(9)本地客户进程接收到消息后,根据其中的过程名将消息存入关联的客户存根,再将控制权转移给客户存根;
(10)客户存根从消息中取出结果,返回给本地调用者进程,并完成控制权的转移。
这样,本地调用者再次获得控制权,并且得到了所需的数据,得以继续运行。显然,
2.6.2 消息传递通信的实现方式
-
直接消息传递系统
(1)直接通信原语
- 对称寻址方式
定义:该方式要求你发送进程和接受进程都必须以显示方式提供对方的标识符;
缺点:一旦改变进程的名称,则可能需要检查所有其他进程的定义,有关对该进程旧名称的所有引用都必须查找到,以便将其修改为新名称,显然,这样的方式不利于实现进定义的模块化。
- 非对称寻址方式
定义:某些情况下,接收进程可能需要与多个发送进程通信,无法事先指定发送过程;
(2)消息的格式
在单机系统环境中,由于发送进程和接受进程处于同一台机器中,有着相同的环境,所以消息的格式比较简单,可采用比较短多额定长消息格式,以减少对消息的处理和存储开销。
(3)进程的同步方式
- 发送进程阻塞、接受进程阻塞
- 发送进程不阻塞、接受进程阻塞:应用最广的一种进程同步方式;
- 发送进程和接受进程均不阻塞
(4)通信链路
第一种通信方式:
两种方式建立通信链路。第一种方式是:由发送进程在通信之前用显式的“建立连接”命令(原语)请求系统为之建立一条通信链路,在链路使用完后拆除链路。这种方式主要用于计算机网络中。
第二种通信方式:
计算机网络中。第二种方式是:发送进程无须明确提出建立链路的请求,只须利用系统提供的发送命令(原语),系统会自动地为之建立一条链路。这种方式主要用于单机系统中。
而根据通信方式的不同,则又可把链路分成两种:
① 单向通信链路,只允许发送进程向接收进程发送消息,或者相反;
② 双向通信链路,既允许由进程A向进程B发送消息,也允许进程B同时向进程A发送消息。
-
信箱通信
每个信箱都有一个唯一的标识符,利用邮箱通信方式既可以实现实时通信,又可以实现非实时通信;
(1)信箱的结构
信箱定义为一种数据结构,逻辑上可分为两部分;
(1)信箱头
用以存放有关信箱的描述信息,如信箱标识符、信箱的拥有者、信箱口令、信箱的空格数等;
(2)信箱体
由若干个可以存放消息(或消息头)的信箱格组成,信箱格的数目以及每格的大小是在创建信箱时确定的。
双向信箱示意图:
(2)信箱通信原语
(1)邮箱的创建和撤消。进程可利用邮箱创建原语来建立一个新邮箱,创建者进程应给出邮箱名字、邮箱属性(公用、私用或共享);对于共享邮箱,还应给出共享者的名字。当进程不再需要读邮箱时,可用邮箱撤消原语将之撤消。
(2)消息的发送和接收。当进程之间要利用邮箱进行通信时,必须使用共享邮箱,并利用系统提供的下述通信原语进行通信。(3)信箱的类型
(1)私用邮箱
用户进程可为自己建立一个新邮箱,并作为该进程的一部分。采用单向通信链路的邮箱来实现。
(2)公用邮箱
由操作系统创建,并提供给系统中的所有核准进程使用。核准进程既可把消息发送到该邮箱中,也可从邮箱中读取发送给自己的消息。采用双向通信链路的邮箱来实现。
(3)共享邮箱
由某进程创建,在创建时或创建后指明它是可共享的,同时须指出共享进程(用户)的名字。邮箱的拥有者和共享者都有权从邮箱中取走发送给自己的消息。
在利用邮箱通信时,在发送进程和接收进程之间,存在以下四种关系:
① 一对一关系发送进程和接收进程可以建立一条两者专用的通信链路,使两者之间的交互不受其他进程的干扰;
② 多对一关系。允许提供服务的进程与多个用户进程之间进行交互,也称为客户/服务器交互(client/server interaction);
③ 一对多关系。允许一个发送进程与多个接收进程进行交互,使发送进程可用广播方式向接收者(多个)发送消息;
④ 多对多关系。允许建立一个公用邮箱,让多个进程都能向邮箱中投递消息;也可从邮箱中取走属于自己的消息。
2.6.3 直接消息传递系统实例
-
消息缓冲队列通信机制中的数据结构
(1)消息缓冲区
语言描述:
(2)PCB中有关通信的数据项
在操作系统中采用了消息缓冲队列通信机制时,除了需要为进程设置消息缓冲队列外,还应在进程的PCB中增加消息队列队首指针,用于对消息队列进行操作,以及用于实现同步的互斥信号量mutex和资源信号量sm。在PCB中应增加的数据项可描述如下:
-
发送原语
发送进程在利用发送原语发送消息之前,应先在自己的内存空间设置一发送区a,如下图所示,把待发送的消息正文、发送进程标识符、消息长度等信息填入其中,然后调用发送原语,把消息发送给目标(接收)进程。发送原语首先根据发送区a中所设置的消息长度a.size来申请一缓冲区i,接着,把发送区a中的信息复制到缓冲区i中。为了能将i挂在接收进程的消息队列mq上,应先获得接收进程的内部标识符j,然后将i挂在j.mq上。由于该队列属于临界资源,故在执行insert操作的前后都要执行wait和 signal操作。
发送原语描述:
-
接受原语
接收进程调用接收原语receive(b),从自己的消息缓冲队列mq中摘下第一个消息缓冲区i,并将其中的数据复制到以b为首址的指定消息接收区内。接收原语描述如下:
2.7 线程的基本概念
2.7.1 线程的引入
引入目的:为了减少程序在并发执行时所付出的时空开销,使OS具有更好的并发性;
-
进程的两个基本属性
① 进程是一个可拥有资源的独立单位,一个进程要能独立运行,它必须拥有一定的资源,包括用于存放程序正文、数据的磁盘和内存地址空间,以及它在运行时所需要的I/O设备、已打开的文件、信号量等;
② 进程同时又是一个可独立调度和分派的基本单位,一个进程要能独立运行,它还必须是一个可独立调度和分派的基本单位。
-
程序并发执行时所需付出的时空开销
(1)创建进程:系统在创建一个进程时,必须为它分配其所必需的、除处理机以外的所有资源,如内存空间、IO设备,以及建立相应的PCB;
(2)撤销进程:系统在撤消进程时,又必须先对其所占有的资源执行回收操作,然后再撤消PCB;
(3)进程切换:对进程进行上下文切换时,需要保留当前进程的CPU环境,设置新选中进程的CPU环境,因而须花费不少的处理机时间。
-
线程—作为调度和分派的基本单位
2.7.2 线程与进程的比较
-
调度的基本单位
传统OS;进程是作为独立调度和分派的基本单位;
引入线程的OS:线程作为调度和分派的基本单位,线程切换时,仅需保存和设置少量寄存器内容,切换代价远低于进程,且同一进程的线程切换不会引起进程切换,不同进程之间的线程切换必然引起进程的切换。
-
并发性
一个进程中的多个线程之间亦可并发执行,甚至允许一个进程中的所有线程都能并发执行,不同进程之间的线程也能并发执行;
-
拥有资源
进程可以拥有资源,并作为系统中拥有资源的一个基本单位。然而,线程本身并不拥有系统资源,而是仅有一点必不可少的、能保证独立运行的资源。比如,在每个线程中都应具有一个用于控制线程运行的线程控制块TCB、用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
线程除了拥有自己的少量资源外,还允许多个线程共享该进程所拥有的资源,这首先表现在:属于同一进程的所有线程都具有相同的地址空间,这意味着,线程可以访问该地址空间中的每一个虚地址;此外,还可以访问进程所拥有的资源,如已打开的文件、定时器、信号量机构等的内存空间和它所申请到的IO设备等。
-
独立性
同一进程中的不同线程之间的独立性要比不同进程之间的独立性低的多。
-
系统开销
线程之间的切换以及创建都比进程快上好多倍。此外,由于一个进程中的多个线程具有相同的地址空间,线程之间的同步和通信也比进程的简单。因此,在一些OS中,线程的切换、同步和通信都无需操作系统内核的干预。
-
支持多处理机系统
2.73 线程的状态和线程控制块
-
线程运行的三个状态
(1)执行状态:表示线程已获得处理机而正在运行;
(2)就绪状态:指线程已具备了各种执行条件,只须再获得CPU便可立即执行;
(3)阻塞状态:指线程在执行中因某事件受阻而处于暂停状态,例如,当一个线程执行从键盘读入数据的系统调用时,该线程就被阻塞。
线程状态之间的切换同进程状态之间的切换是一样的。
-
线程控制块TCB
线程控制块通常由以下几项
① 线程标识符:为每个线程赋予一个唯一的线程标识符;
② 一组寄存器:包括程序计数器PC、状态寄存器和通用寄存器的内容;
③ 线程运行状态:用于描述线程正处于何种运行状态;
④ 优先级:描述线程执行的优先程度;
⑤ 线程专有存储区:用于线程切换时存放现场保护信息,和与该线程相关的统计信息等;
⑥ 信号屏蔽:即对某些信号加以屏蔽;
⑦ 堆栈指针:在线程运行时,经常会进行过程调用,而过程的调用通常会出现多重嵌套的情况,这样,就必须将每次过程调用中所使用的局部变量以及返回地址保存起来。解决办法:为每个线程设置一个堆栈,用它来保存局部变量和返回地址
在TCB中,也须设置两个指向堆栈的指针:指向用户自己堆栈的指针和指向核心栈的指针。前者是指当线程运行在用户态时,使用用户自己的用户栈来保存局部变量和返回地址,后者是指当线程运行在核心态时使用系统的核心栈。
-
多线程OS中的进程属性
(1)进程是一个可拥有资源的基本单位
任一进程所拥有的资源都包括:用户的地址空间、实现进程(线程)间同步和通信的机制、已打开的文件和已申请到的I/O设备,以及一张由核心进程维护的地址映射表,该表用于实现用户程序的逻辑地址到其内存物理地址的映射。
(2)多个线程可以并发执行
(3)进程已不是可自行的实体
例如,在把某个进程挂起时,该进程中的所有线程也都将被挂起;又如,在把某进程激活时,属于该进程的所有线程也都将被激活。
2.8 线程的实现
2.8.1 线程的实现方式
-
内核支持线程
内核根据该控制块而感知某线程的存在,并对其加以控制,切当前大多数OS都支持内核支持线程;
该线程实现方式优点:
(1)在多处理器系统中,内核能够同时调度同一进程中的多个线程并行执行;
(2)如果进程中的一个线程被阻塞了,内核可以调度该进程中的其它线程占有处理器运行,也可以运行其它进程中的线程;
(3)内核支持线程具有很小的数据结构和堆栈,线程的切换比较快,切换开销小;
(4)内核本身也可以采用多线程技术,可以提高系统的执行速度和效率。
主要缺点:
对于用户的线程切换而言,其模式切换的开销较大,在同一个进程中,从一个线程切换到另一个线程时,需要从用户态转到核心态进行,这是因为用户进程的线程在用户态运行,而线程调度和管理是在内核实现的,系统开销较大。
-
用户级线程
用户级线程是在用户空间中实现的。对线程的创建、撤消、同步与通信等功能,都无需内核的支持,即用户级线程是与内核无关的。
由于这些线程的任务控制块都是设置在用户空间,而线程所执行的操作也无需内核的帮助,因而内核完全不知道用户级线程的存在。
值得说明的是,对于设置了用户级线程的系统,其调度仍是以进程为单位进行的。
假如系统中设置的是内核支持线程,则调度便是以线程为单位进行的。
该线程实现方式优点:
(1)线程切换不需要转换到内核空间
(2)调度算法可以是进程专用的
(3)用户级线程的实现与OS平台无关
主要缺点:
(1)系统调用的阻塞问题
(2)在单纯的用户级线程实现方式中,多线程应用不能利用多处理机进行多重处理的优点,内核每次分配给一个进程的仅有一个CPU,因此,进程中仅有一个线程能执行,在该线程放弃CPU 之前,其它线程只能等待。
-
组合方式
(1)多对一模型
定义:即将用户线程映射到一个内核控制线程。
主要优点:线程管理的开销小,效率高;
主要缺点:如果一个线程在访问内核时发生阻塞,则整个进程都会被阻塞;此外,在任一时刻,只有一个线程能够访问内核,多个线程不能同时在多个处理机上运行。
模型图:
(2)一对一模型
定义:
主要优点:当一个线程阻塞时,允许调度另一个线程运行,所以它提供了比多对一模型更好的并发功能。此外,在多处理机系统中,它允许多个线程并行地运行在多处理机系统上。
主要缺点:每创建一个用户线程,相应地就需要创建一个内核线程,开销较大,因此需要限制整个系统的线程数。
模型图:
(3)多对多模型
定义:即将许多用户线程映射到同样数量或更少数量的内核线程上。
优点:它可以像一对一模型那样,使一个进程的多个线程并行地运行在多处理机系统上,也可像多对一模型那样,减少线程的管理开销和提高效率。
模型图:
2.8.2 线程的实现
-
内核支持线程的实现
其中一种控制方法是,系统在创建一个新进程时,便为它分配一个任务数据区PTDA,其中包括若干个TCB空间;
如下图:
内核支持线程的调度和切换与进程的调度和切换十分相似,也分抢占式方式和非抢占式两种。在线程的调度算法上,同样课采用时间片轮转法、优先权算法等。但线程调度选中一个线程后,便将处理机分配给它。当然,线程在调度和切换上所花费的开销要比进程的小得多。
-
用户级线程的实现
(1)运行时系统
所谓“运行时系统”,实质上是用于管理和控制线程的函数(过程)的集合,其中包括用于创建和撤消线程的函数、线程同步和通信的函数,以及实现线程调度的函数等。正因为有这些函数,才能使用户级线程与内核无关。运行时系统中的所有函数都驻留在用户空间,并作为用户级线程与内核之间的接口。
(2)内核控制线程
别称:轻型进程LWP(Light Weight Process)
每一个进程都可拥有多个LWP,同用户级线程一样;
每个LWP都有自己的数据结构(如TCB),其中包括线程标识符、优先级、状态,另外还有栈和局部存储区等。LWP也可以共享进程所拥有的资源。
利用轻型进程作为中间系统时用户级线程的实现方法:
2.8.3 线程的创建和终止
-
线程的创建
第一个线程称为:“初始化线程”;
它的主要功能是用于创建新线程。在创建新线程时,需要利用一个线程创建函数(或系统调用),并提供相应的参数,如指向线程主程序的入口指针、堆栈的大小,以及用于调度的优先级等。在线程的创建函数执行完后,将返回一个线程标识符供以后使用。
-
线程的终止
当一个线程完成了自己的任务(工作)后,或是线程在运行中出现异常情况而须被强行终止时,由终止线程通过调用相应的函数(或系统调用)对它执行终止操作。但有些线程(主要是系统线程),它们一旦被建立起来之后,便一直运行下去而不被终止。在大多数的OS中,线程被中止后并不立即释放它所占有的资源,只有当进程中的其它线程执行了分离函数后,被终止的线程才与资源分离,此时的资源才能被其它线程利用.。
定义:即将许多用户线程映射到同样数量或更少数量的内核线程上。
优点:它可以像一对一模型那样,使一个进程的多个线程并行地运行在多处理机系统上,也可像多对一模型那样,减少线程的管理开销和提高效率。
模型图:
[外链图片转存中…(img-JVqeFXS5-1597833899360)]
2.8.2 线程的实现
-
内核支持线程的实现
其中一种控制方法是,系统在创建一个新进程时,便为它分配一个任务数据区PTDA,其中包括若干个TCB空间;
如下图:
[外链图片转存中…(img-8jPl94m5-1597833899360)]
内核支持线程的调度和切换与进程的调度和切换十分相似,也分抢占式方式和非抢占式两种。在线程的调度算法上,同样课采用时间片轮转法、优先权算法等。但线程调度选中一个线程后,便将处理机分配给它。当然,线程在调度和切换上所花费的开销要比进程的小得多。
-
用户级线程的实现
(1)运行时系统
所谓“运行时系统”,实质上是用于管理和控制线程的函数(过程)的集合,其中包括用于创建和撤消线程的函数、线程同步和通信的函数,以及实现线程调度的函数等。正因为有这些函数,才能使用户级线程与内核无关。运行时系统中的所有函数都驻留在用户空间,并作为用户级线程与内核之间的接口。
(2)内核控制线程
别称:轻型进程LWP(Light Weight Process)
每一个进程都可拥有多个LWP,同用户级线程一样;
每个LWP都有自己的数据结构(如TCB),其中包括线程标识符、优先级、状态,另外还有栈和局部存储区等。LWP也可以共享进程所拥有的资源。
利用轻型进程作为中间系统时用户级线程的实现方法:
[外链图片转存中…(img-sApB8yyt-1597833899361)]
2.8.3 线程的创建和终止
-
线程的创建
第一个线程称为:“初始化线程”;
它的主要功能是用于创建新线程。在创建新线程时,需要利用一个线程创建函数(或系统调用),并提供相应的参数,如指向线程主程序的入口指针、堆栈的大小,以及用于调度的优先级等。在线程的创建函数执行完后,将返回一个线程标识符供以后使用。
-
线程的终止
当一个线程完成了自己的任务(工作)后,或是线程在运行中出现异常情况而须被强行终止时,由终止线程通过调用相应的函数(或系统调用)对它执行终止操作。但有些线程(主要是系统线程),它们一旦被建立起来之后,便一直运行下去而不被终止。在大多数的OS中,线程被中止后并不立即释放它所占有的资源,只有当进程中的其它线程执行了分离函数后,被终止的线程才与资源分离,此时的资源才能被其它线程利用.。