目录
2.3.1 进程同步的概念
2.3.2实现临界区互斥的基本方法
2.3.3信号量机制
2.3.4管程
在多道程序中,不同进程的并发执行存在着不同的相互制约关系,为了协调这种关系,使各个进程按照一定的规划共享资源互相合作,从而保证并发的进程具有可再现性,引入了进程同步机制。
(1) 直接制约关系(同步)
当进程的运行需要其他进程相互合作,交互信息时,这些进程之间就是直接制约关系,即若一个进程无法收到另一个进程提供的信息,该进程便无法运行。
[eg]:进程A通过单缓冲向进程B提供数据。当该缓冲区空时,进程B不能获得所需数据而阻塞,一旦进程 A将数据送入缓冲区,进程B就被唤醒。反之,当缓冲区满时,进程A被阻塞,仅当进程B取走缓冲数据时,才唤醒进程A。
(2)间接制约关系(互斥)
当一个进程要使用某种共享资源时,而该资源正在被另一个进程占用,并且该资源不允许两个进程同时使用,则该进程必须等待,当占用资源的进程释放资源后才允许去使用。
[eg]:在仅有一台打印机的系统中,若进程A需要打印时,系统已将打印机分配给进程B,则进程A必须阻塞。一旦进程B将打印机释放,系统便将进程A唤醒,并将其由阻塞态变为就绪态。
(3)临界资源和临界区
系统中很多进程都可以共享系统中的资源,但其中很多资源在一段时间内只能运行一个进程,这种资源就叫做临界资源。常见的硬件临界资源有打印机,磁带器等,此外还有许多变量,数据等软件临界资源。
对临界资源的访问必须互斥地进行。在进程中,访问临界资源的那段代码称为临界区。访问临界资源的过程:
①进入区。检查能否进入临界区,若能则设置正在访问的标志(下一节详细介绍如何实现标志),防止其他进程进入。
②临界区。进程中访问临界资源的那段代码。
③退出区。将正在访问临界区的标志清楚。
④剩余区。代码中的其他部分。
为禁止两个进程同时进入临界区,同步机制应遵循以下准则:
1)空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
2)忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待。
3)有限等待。对请求访问的进程,应保证能在有限时间内进入临界区。
4)让权等待。当进程不能进入临界区时,应立即释放处理器,防止进程忙等待。
【习题】:
1.一个正在访问临界资源的进程由于申请等待IO操作而被中断时,它( )。C
A.允许其他进程进入与该进程相关的临界区 B. 不允许其他进程进入任何临界区
C.允许其他进程抢占处理器,但不得进入该进程的临界区 D.不允许任何进程抢占处理器
··系统中进程进入临界区但因某些原因阻塞的情形经常发生,这种情况下,只要其他进程不寻求进入该进程的临界区,就允许运行,也就可以为其分配处理机。但此时临界区依旧是锁上的,仍然是不允许其他进程访问的,只能期望该进程在离开时能将临界区给它。
2.以下( )属于临界资源。B
A.磁盘存储介质 B.公用队列 C.私用数据 D.可重入的程序代码
··临界资源是一段时间内只允许被一个进程访问的资源。磁盘存储介质属于共享设备,可被多个进程访问;公用队列可以供多个进程使用,但一次只能有一个进程使用,否则会导致队列中的数据混乱无法使用;私用数据本就只能被一个进程使用,不存在临界区的问题;可重入的程序代码一次可以供多个进程使用。
3. 进程A和进程B通过共享缓冲区协作完成数据处理,进程A负责产生数据并放入缓冲
区,进程B从缓冲区读数据并输出。进程A和进程B之间的制约关系是( ).C
A.互斥关系 B.同步关系 C.互斥和同步关系 D.无制约关系
··进程AB通过共享缓冲区完成数据处理,共享缓冲区的访问一定是互斥的,所以AB具有互斥关系;同时AB相互制约,只有A产生数据放入缓冲区,B才能读数据输出,二者是同步的制约关系
4. [2020统考真题]下列准则中,实现临界区互斥机制必须遵循的是( )。ABC
A.两个进程不能同时进入临界区 B.允许进程访问空闲的临界资源
C.进程等待进入临界区的时间是有限的 D.不能进入临界区的执行态进程立即放弃CPU
··ABCD选项分别对应“忙则等待”,“空闲让进”,“有限等待”,“让权等待”四个实现临界区互斥的准则,前三者是互斥机制必须遵循的准则,“让权等待”不一定非得实现,比如Perterson算法。
(1)软件实现
①单标志法
该算法设置了一个公用整型变量turn,表示允许进入临界区的进程编号。例如有P1,P2两个进程访问同一临界资源:
[特点]:单标志法保证了对临界资源的互斥访问,但当两个进程必须交替进入临界区时容易造成“空闲让进”的现象(即P0退出后将turn置为1,但P1暂时不需要访问资源时,P0就无法再次进入临界区访问资源),造成资源利用不充分。
②双标志先检查
该算法设置了一个数据flag[i],在每个进程进入临界区之前,先检查临界区资源是否正被访问,当第i个元素值为FALSE,表示Pi未进入临界区,TURE则表示进入临界区。
[特点]:双标志法不用交替进入临界区,解决了“空闲让进”的问题,但是当两个进程按照①③②④的顺序执行时(检查对方的flag后没有及时切换到修改自己的flag转而进行另一进程的检查中)可能同时进入临界区,这就违背了“忙则等待”的原则。
③双标志后检查
改算法在双标志先检查算法基础上修改检查顺序,即先将自己的标志设为TURE,再来检测对方的状态标志,若对方为TURE,则让对方进入临界区,自己等待。
[特点]:该算法解决了两个进程同时进入临界区的现象,但是会发生两个进程都不进入的“饥饿”现象,即它们分别将标志设置为TURE后同时检测对方的状态发现对方也要进入临界区,则双方都进入等待,临界区一直空闲,这就违背了“空闲让进,有限等待”原则。
④Peterson’s Algorithm
本算法综合双标志的先后检查法,为了避免两个进程的无限等待,增加一个turn变量,用来表示是否允许进入临界区。
[特点]:算法结合上述算法,利用flag解决临界资源的互斥访问,又利用turn标志解决了“饥饿”现象。
(2)硬件实现
①中断屏蔽
CPU的进程切换是通过中断完成的,所以屏蔽中断可以可以让当前正在运行的进程顺利执行,防止其他进程进入临界区,进而保证互斥的实现,典型步骤为:关中断→临界区→开中断。
[特点]:该方法简单高效,但是只适用于操作系统的内核进程,不适用于用户进程;处理机的执行效率下降;若关中断后没有开中断系统可能会终止。
②硬件指令
TestAndSet指令:该指令是原子操作(执行过程不允许被中断),用于将读出的指定标志设为真
Swap指令:用于交换两个字节的内容
为了采用更加简单合理的方法解决互斥与同步问题,Dijkstra提出了信号量同步机制,主要采用标准原语(wait(S) and signal(S);or记为P操作and V操作)来解决多个相互合作的进程之间的信号同步。
(1)整型信号量
整型信号量被定义为一个表示资源数目的整型量S,用wait and signal操作,通过P操作申请进入临界区访问资源,V操作用来释放资源:
wait(S) { //申请资源,资源数-1
while(S<=0); //只要信号量小于0,就会不停的测试
S=S-1;
}
signal(S) { //释放资源,资源数+1
S=S+1;
}
该机制只是让进程处于忙等待的状态,违背了“让权等待”的原则。
(2)记录型信号量
为了解决忙等的现象,记录型信号量采用一种数据结构来记录访问资源的信息。该数据结构由一个表示资源数目的整型变量value和一个用于链接所有等待该资源进程的链表L,具体表示
typedef struct {
int value;
Struct process *L;
}semaphore;
wait(S)操作:
void wait (semaphore S) { //申请资源,可供分配资源数-1
S.value--;
If(S.value<0){ //当S.value<0时,资源已经分配完毕,插入到等待队列S.L中,并自我阻塞
Add this process to S.L;
block(S.L);
}
}
signal(S)操作:
void signal (semaphore S) { //释放资源,可供分配的资源数+1
S.value++;
If(S.value<=0){ //当S.value<=0时,表示仍有等待该资源的进程阻塞,故唤醒队列第一个等待进程
Remove a process P from S.L;
wakeup(P);
}
}
(3)信号量的应用
①利用信号量实现同步
在两个并发的进程中增加一变量S,表示两进程同步的公共信号量,初值为0。例如进程P2中的语句S2要使用进程P1中S1的结果,则只有当S1执行完毕后,S2才可以执行,如下:
semaphore S=0; //设置公共信号量,并初始化
P1(){
...
S1; //语句1
V(S); //释放资源,并告诉P2,语句S1已经完成
}
P2(){
...
P(S); //申请资源,检查S1是否运行完成
S2; //检查无误,则运行S2
}
[分析]:若P1先执行到V操作,S+1=1,之后执行到P2的P操作,由于S=1,表示有资源可用,不会执行block原语自我阻塞,执行S--,S的值变回0,最后往下执行S2;
若先执行到P2的P操作,S--后值为-1,表示没有可用资源,此时P操作会执行block原语主动阻塞,放入阻塞队列,当S1执行完后,继而执行V操作,S++后值为0,由于此时由进程位于阻塞队列中,V操作会执行wakeup原语唤醒P2进程,然后继续执行S2。
②利用信号量实现进程互斥
进程P1,P2并发执行,二者有各自的临界区,但系统要求每次只能又一个进程进入自己的临界区,因此设置信号量S初值为1(可用资源数为1),把临界区置于P,V操作之间,实现两个进程对临界资源的互斥访问:
semaphore S=1; //初始化信号量
P1(){
...
P(S); //申请访问临界资源,并加锁
P1的临界区;
V(S); //访问结束,释放资源,解锁
}
P2(){
...
P(S); //申请访问临界资源,并加锁
P2的临界区;
V(S); //访问结束,释放资源,解锁
}
[分析]:互斥是不同进程对同一信号量的操作实现,每个进程的PV操作成对出现,若临界区中没有进程,任意进程进入就要执行P操作,加锁S--为0,然后进入;若临界区中有进程,此时S为0,想要执行P操作便会自我阻塞,直到临界区的进程退出,这样就实现了互斥。
③利用信号量实现前驱关系
信号量也可以用来描述程序或语句之间的前驱关系,如图下图所示,为各程序段Si设置若干初始值为0 的信号量,保证每一个前驱关系的执行,部分实现:
semaphore a=b=c=d=e=f=g=0;
S1(){
...
V(a);V(b); //S1此时已经完成
}
S2(){
P(a); //检查S1是否完成
...
V(c);V(d); //S2此时已经完成
}
...
...
【习题】:
1.有三个进程共享同一程序段,而每次只允许两个进程进入该程序段,若用PV操作同步机制,则信号量S的取值范国是( ).A
A. 2,1,0,-1 B.3,2,1,0 C. 2,1,0,-1,-2 D.1,0,-1,-2
··由于每次只允许两个进程进入程序段,所以信号量的值最大为2;当三个进程申请进入该程序段时,进行三次P操作,信号量有最小值-1
2. 对于两个并发进程,设互斥信号量为mutex (初值为1),若mutex=0,则表示 ()B
对于两个并发进程,设互斥信号量为mutex (初值为1),若mutex=-1, 则 ()C
A.表示没有进程进入临界区 B.表示有一个进程进入临界区
C.表示有一个进程进入临界区,另一个进程等待进入 D.表示有两个进程进入临界区
··mutex初值为1,代表只允许一个进程进入临界区,当一个进程进入临界区而此时没有其他进程等待进入mutex-1=0
··当mutex<0时,其绝对值表示等待进入临界区的进程数
3. 有一个计数信号量S:
1)假如若干进程对S进行28次P操作和18次V操作后,信号量S的值为0.
2)假如若干进程对信号量S进行了15次P操作和2次V操作。请问此时有多少个进
程等待在信号量S的队列中? ( )B
A.2 B.3 C.5 D.7
··由1)可知:S-28+18=0,得S的初值为10;
··由2)可知:S-15+2=-3,所以此时有3个进程在等待队列中
4. 有两个并发进程P和P2。其程序代码如下:
可能打印出的z值有( ), 可能打印出的c值有( )(其中x为P1, P2的共享变量).
A. z-1,-3; c=-1,9 B. z=-1,3; c=1,9 C. z=-1,3,1; c=9 D. z=3; c=1,9
··x为共享变量,所以A1,B1执行顺序的不同会导致x的值有所不同,即1和-3;所以z的值可能为1+2=3 or -3+2=-1,c的值同理
5. 有两个优先级相同的并发程序P和P2,它们的执行过程如下所示。假设当前信号量s1 =0,s2=0。当前的z=2,进程运行结束后,x,y和z的值分别是( ).C
A.5,9,9 B. 5,9,4 C5,12,9 D.5,12,4
··由于进程并发,在P1,P2的PV操作之前,两个进程的运行互不干扰,此时xyz分别为2,3,4;因为进程P2执行P(S1)操作,所以必须等待P1中V(S1)执行完成后才能继续运行;P1进程执行完V(S1)后在P(S2)阻塞,P2继续执行,此时xyz为5,3,9;待V(S2)执行完P1继续执行直至结束,最终xyz为5,12,9
6. [2011统考真题]有两个并发执行的进程P1和进程P2,共享初值为1的变量x。P1对、
加1, P2对x减1.加1和减1操作的指令序列分别如下:
A.可能为-1或3 B.只能为1 C.可能为0,1或2 D.可能为-1,0,1成2
··将P1中的3条语句依次编号为1, 2, 3,将P2中的3条语句依次编号为4, 5, 6,则依次执行1,2,3,4,5,6得结果1,依次执行1,2,4,5,6,3得结果2,依次执行4,5, 1,2,3,6得结果0。-1不可能得出。
7. [2016统考真题]进程P和P2均包含并发执行的线程,部分伪代码描述如下所示:
下列选项需要互斥执行的操作是:
A. a=1与a=2 B.a=x与b=x C. x+=1与x+=2 D. x+=1与x+=3
··P1中对a进行赋值,并不影响最终的结果,因此a=1与a=2不需要互斥执行;
··a=x与b=x执行先后不影响a与b的结果,无须互斥执行;
··x+=1 与x+=2执行先后会影响x的结果,需要互斥执行;
··P1中的x和P2中的x是不同范围中的x,互不影响,不需要互斥执行
8. [2018统考真题]属于同- -进程的两个线程threadl 和thread2 并发执行,共享初值为0的金局变量x。threadl 和thread2实现对全局变量x加1的机器级代码描述如下。在所有可能的指令执行序列中,使x的值为2的序列个数是( )。B
A.1 B.2 C.3 D.4
··将thread1中的3条语句依次编号为1, 2, 3,将thread2中的3条语句依次编号为4, 5, 6,则依次执行1,2,3,4,5,6得结果2,依次执行4,5,6,1,2,3得结果2,所以使x的值为2的序列有2个
在信号量机制中,大量分散的同步PV操作各系统的管理带来不便,还可能因操作不当导致系统死锁。于是产生了另一种进程同步工具—管程。管程不仅保证了进程互斥,还提供了条件变量,让程序员灵活的实现进程同步
(1)管程的定义
利用共享数据结构抽象地表示系统中的共享资源,而把对该数据结构实施的操作定义为过程。进程对共享资源的申请、释放等操作,都通过这组过程来实现。这个代表共享资源的数据结构,以及由对该共享数据结构共享资源实施操作的一组过程所组成的资源管理程序,称为管程。管程定义了一个数据结构和能为并发进程所执行在该数据结构上的一组操作,这组操作能同步进程和改变管程中的数据,eg:
monitor Demo{ //定义一个名为demo的管程
共享数据结构S; //定义一个共享数据结构
init_code(){ //对共享数据结构初始化
S=5;
}
take_away(){ //过程:申请一个资源
对共享数据结构的一系列处理;
S--; //可用资源-1
...
}
give_back(){ //过程:归还一个资源
对共享数据结构的一系列处理;
S++; //可用资源+1
...
}
}
(2)管程的组成
由上述的定义可以知道管程的四个部分:
①管程的名称
②局部于管程内部的共享结构数据说明
③对该数据结构进行操作的一组过程(或函数)
④对局部于管程内部的共享数据设置初始值的语句
(3)管程的特性
①管程内的局部变量只能被局限于管程内的过程所访问;反之亦然,即局部于管程内的过程只能访问管程内的变量。
②任何进程只能通过调用管程提供的过程入口进入管程。对于上例中外部进程只能通过take_away()过程来申请一个资源。
③任一时刻,最多只能有一个进程在管程中执行,从而实现进程互斥。管程是一种编程语言的构件,它的实现需要编译器的支持。
(4)条件变量
当一个进程进入管程后被阻塞,直到阻塞的原因解除时,在此期间,如果该进程不释放管程,那么其他进程无法进入管程。为此,将阻塞原因定义为条件变量condition。通常,一个进程被阻塞的原因可以有多个,因此在管程中设置了多个条件变量。每个条件变量保存了一个等待队列,用于记录因该条件变量而阻塞的所有进程,对条件变量只能进行两种操作,即wait和signal.
x.wait:当x对应的条件不满足时,正在调用管程的进程调用x.wait将自己插入x条件的等待队列,并释放管程。此时其他进程可以使用该管程。
x.signal:x对应的条件发生了变化,则调用x.signal,唤醒一个因 x条件而阻塞的进程。
[条件变量和信号量]:
①条件变量的wait&signal类似于信号量的PV操作,实现进程的阻塞唤醒。
②条件变量没有值,仅通过判断共享数据结构有无实现“排队等待”功能;信号量是有值的,反映剩余的资源数目
【习题】:
1. 以下关于管程的叙述中,错误的是( )。
A.管程是进程同步工具,解决信号量机制大量同步操作分散的问题
B.管程每次只允许一个进程进入管程
C.管程中signal操作的作用和信号量机制中的V操作相同
D.管程是被进程调用的,管程是语法范围,无法创建和撒销
· ·AB选项在上文中均已提及;管程的signal操作与信号量机制中的V操作不同,信号量机制中的V操作一定会改变信号量的值,而管程中的signal操作是针对某个条件变量的,若不存在因该条件而阻塞的进程,则signal不会产生任何影响;管程定义了一个数据结构和能为并发进程所执行在该数据结构上的一组操作,它定义了数据该怎么用,数据的名字叫什么,但是没有把数据直接放进去,因此说管程是语法范围,或者将管程比作一个函数,你只能传入参数,由进程去调用,但是你无法去创建或者撤销
2. [2016统考真题]下列关于管程的叙述中,错误的是( )。
A.管程只能用于实现进程的互斥 B.管程是由编程语言支持的进程同步机制
C.任何时候只能有一个进程在管程中执行 D.管程中定义的变量只能被管程内的过程访问
··管程不仅能实现进程的互斥,还能实现进程同步 A错误B正确;CD在上文管程的特性已经介绍
3. [2018统考真题]若x是管程内的条件变量,则当进程执行x.wait()时所做的工作是( )。
A.实现对变量x的互斥访问 B.唤醒一个在x上阻塞的进程
C.根据x的值判断该进程是否进入阻塞态 D.阻塞该进程,并将之插入x的阻塞队列中
··当x对应的条件不满足时,正在调用管程的进程调用x.wait将自己插入x条件的等待队列,并释放管程,其他进程可以使用该管程