CH3讲义剖析.docx
《CH3讲义剖析.docx》由会员分享,可在线阅读,更多相关《CH3讲义剖析.docx(33页珍藏版)》请在冰豆网上搜索。
CH3讲义剖析
本章知识点:
1、进程的顺序性和并发性
2、分析与时间有关的错误。
(★)
3、进程同步与互斥的概念。
4、临界区及其调度。
(★)
5、记录型信号量与PV操作。
(★★★)
6、条件变量和管程。
(★★)
7、进程通信种类
8、消息传递的两种方式及其原语。
(★)
本章思考题:
1.叙述顺序程序设计的特点以及采用顺序程序设计的优缺点。
2.程序并发执行为什么会失去封闭性和结果可再现性?
3.叙述并发程序设计的特点以及采用并发程序设计的优缺点。
4.解释并发进程的无关性和交往性。
5.并发进程的执行可能产生与时间有关的错误,试各举一例来说明与时间有关错误的两种表现形式。
6.解释进程的竞争关系和协作关系。
7.试说明进程的互斥和同步两个概念之间的区别。
8.什么是临界区和临界资源?
对临界区管理的基本原则是什么?
9.哪些硬件设施可以实现临界区管理?
10.什么是信号量?
如何对它进行分类。
11.为什么PV操作均为不可分割的原语操作?
12.从信号量和PV操作的定义,可以获得哪些推论?
13.叙述AND型信号量机制的特点
14.叙述一般信量机制的特点
15.有三个并发进程:
R负责从输入设备读入信息,M负责对信号加工处理;P负责打印输出。
今提供;
1)一个缓冲区,可放置K个信息;
2)二个缓冲区,每个可放置K个信息;
3)试用PV操作写出三个进程正确工作的流程
16.没有n个进程共享一个互斥段,如果:
1)每次只允许一个进程进入互斥段;
2)每次最多允许m个进程(m≤n)同时进入互斥段。
试问:
所采用的互斥信号量初值是否相同?
信号量值的变化范围如何?
17.三个进程并发活动,其算法描述如下,试分析是否有错,如有则指出原因并改正。
Begin
S:
=-1;
Cobegin
P1:
Begin
……
V(S);
End
P2:
Begin
……
V(S)
End;
P3:
Begin
P(S)
……
End
Cobegin
End
18.何谓管程?
它有哪些属性?
管程中的过程在执行中能被中断吗?
为什么?
19.试比较Hanson和Hoare两种管程实现方法。
20.已经有PV操作可用作同步工具,为什么还要有消息传递机制。
21.叙述信件,信箱和间接通信原语。
22.简述消息缓冲通信机制的实现思想。
23.什么是管道(pipeline)?
如何通过管道机制实现进程间通信?
24.有一阅览室,读者进入时必须先在一张登记表上登记,该表为每一座位列出一个表目,包括座号、姓名读者离开时要批销登记信息;假如阅览室共有100个座位。
试用:
1)信号量和P.V标作;2)管程;来实现用户进程的同步算法
25.在一个盒子里,混装了数量相等的黑白围棋子。
现在用自动分拣系统把黑子、白子分开,设分拣系统有二个进程P1和P2,其中P1拣白子;P2拣黑子。
规定每个进程每次拣一子;当一个进程在拣时,不允许另一个进程去拣;当一个进程拣了一子时,必须让另一个进程去拣。
试写出两进程P1和P2能并发正确执行的程序。
26.管程的同步机制使用条件变量和Wait及Signal,尝试为管程设计一种仅仅使用一个操作的同步机制。
27.一个快餐厅有4种职员:
(1)领班:
接受顾客点菜;
(2)厨师:
准备顾客的饭菜;(3)打包工:
将做好的饭菜打包;(4)出纳员-收款并提交食品。
每个职员可被看作一个进程,试写出能让四类职员正确并发运行的程序。
28.在信号量S上作P.V操作时,S的值发生变化,当S>0、S=0、S〈0时,它们的物理意义是什么?
29.二个并发进程并发执行,其中,A、B、C、D、E是原语,试给出可能的并发执行路径。
ProcessPProcessQ
beginbegin
A;C;
B;D;
C;end;
end;
是否在任何情况下,‘忙式等待’都比‘阻塞等待’方法的效率低?
试解释之。
30.证明信号量与管程的功能是等价的:
(1)用信号量实现管程;
(2)用管程实现信号量。
31.面包房(BakeryAlgorithm)算法,解决互斥问题的另一个算法:
Varchoosing:
array[0…n-1]ofboolean;
number:
array[0…n-1]ofinteger;
Repeat
Choosing[i]:
=True;
Number[i]:
=1+max(number[0],…nuber[n-1]);
Choosing[i]:
=false;
forj:
=0ton-1do
begin
whilechoosing[j]do{nothing};
whilenumber[j]≠0∧(number[j],j)<(number[i],i)
do{nothing};
end;
{criticalsection};
number[i]:
=0;
{remaider};
forever
其中,choosing和Number的初值分别为false和0,每个数值中的第三个元素只能由进程I进行读和写,而其它进程只能读。
(a,b)<(c,d)的定义为:
(a<c)or(a=c∧b<d)试回答:
(1)用自然语言描述该算法
(2)该算法能否实现互斥,为什么?
CH3进程同步与通信
3.1进程的同步与互斥
3.1.1临界区与临界资源
临界区的概念是由Dijkstra在1965年首先提出的。
一次仅允许一个进程使用的资源称为临界资源。
许多物理设备都属于临界资源,如打印机,绘图机;另外,还有许多变量和数据可以被多个进程共享,也属于临界资源。
访问临界资源的代码成为临界区。
为了保证正确使用资源,可以把对临界资源的访问分为四部分:
进入区、临界区、退出区和剩余区。
对若干个进程共享一个变量的相关的临界区,有三个调度原则:
● 一次至多一个进程能够在它的临界区内;
●不能让一个进程无限地留在它的临界区内;
●不能强迫一个进程无限地等待进入它的临界区。
特别,进入临界区的任一进程不能妨碍正等待进入的其它进程的进展;
我们可把临界区的调度原则总结成四句话:
空闲让进、忙则等待、有限等待、让权等待。
临界区是允许嵌套的,例如
regionxdobegin…regionydo…end
但是粗心的嵌套可能导致进程无限地留在它的临界区内,例如,如果又有一个进程执行:
regionydobegin…regionxdo…end
这样,当两个进程在大约差不多的时间进入了外层的临界区后,将发现它们每个都被排斥在内层临界区之外,造成无限地等待进入临界区。
3.1.2同步与互斥的概念
在多道程序设计系统中,同一时刻可能有许多进程,这些进程之间存在两种基本关系:
竞争关系和协作关系。
第一种是竞争关系,系统中的多个进程之间彼此无关,它们并不知道其他进程的存在。
例如,批处理系统中建立的多个用户进程,分时系统中建立的多个终端进程。
由于这些进程共用了一套计算机系统资源,因而,必然要出现多个进程竞争资源的问题。
当多个进程竞争共享硬设备、变量、表格、链表、文件等资源时,可能导致处理出错。
由于相互竞争资源时进程间并不交换信息,但是一个进程的执行可能影响到同其竞争的进程,如果两个进程要访问同一资源,那么,一个进程通过操作系统分配得到该资源,另一个将不得不等待。
在极端的情况下,被阻塞进程永远得不到访问权,从而不能成功地终止。
所以,资源竞争出现了两个控制问题:
一个是死锁(Deadlock)问题,一组进程如果都获得了部分资源,还想要得到其他进程所占有的资源,最终所有的进程将陷入死锁。
另一个是饥饿(Starvation)问题,例如,三个进程p1、p2、p3均要周期性访问资源R。
若p1占有资源,p2和p3等待资源。
当p1离开临界区时,p3获得了资源R。
如果p3退出临界区之前,p1又申请并得到资源R,如此重复造成p2老是得不到资源R。
尽管这里没有产生死锁,但出现了饥饿。
对于临界资源,操作系统需要保证诸进程能互斥地访问这些资源,既要解决饥饿问题,又要解决死锁问题。
进程的互斥(MutualExclusion)是解决进程间竞争关系的手段。
指若干个进程要使用同一共享资源时,任何时刻最多允许一个进程去使用,其它要使用该资源的进程必须等待,直到占有资源的进程释放该资源。
临界区管理可以解决进程互斥问题,本章第二节将详细介绍临界区的解决方案。
第二种是协作关系,某些进程为完成同一任务需要分工协作。
我们在前面给出了一个例子,input、process、和output三个进程分工协作完成读入数据、加工处理和打印输出任务,这是一种典型的协作关系,各自都知道对方的存在。
这时操作系统要确保诸进程在执行次序上协调一致,没有输入完一块数据之前不能加工处理,没有加工处理完一块数据之前不能打印输出等等,每个进程都要接收到它进程完成一次处理的消息后,才能进行下一步工作。
进程间的协作可以是双方不知道对方名字的间接协作,例如通过共享访问一个缓冲区进行松散式协作;也可以是双方知道对方名字,直接通过通信进行紧密协作。
进程的同步(Synchronization)是解决进程间协作关系的手段。
指一个进程的执行依赖于另一个进程的消息,当一个进程没有得到来自于另一个进程的消息时则等待,直到消息到达才被唤醒。
不难看出,进程互斥关系是一种特殊的进程同步关系,即逐次使用互斥共享资源。
3.2互斥的实现方法
3.2.1互斥算法
3.2.1.1Dekker算法
荷兰数学家T.Dekker算法能保证进程互斥地进入临界区,这是最早提出的一个不需轮换的软件互斥方法,此方法用一个指示器turn来指示应该哪一个进程进入临界区。
若turn=0则进程P0可以进入临界区;若turn=1则进程P1可以进入临界区。
Dekker算法的程序如下:
varinside:
array[0..1]ofBoolean;
Turn:
integer;
turn:
=0or1;
inside[0]:
=false;
inside[1]:
=false;
cobegin
processP0
begin
inside[0]:
=true;
whileinside[1]doifturn=1then
begin
inside[0]:
=false;
whileturn=1dobeginend;
inside[0]:
=true;
end
临界区;
turn=1;
inside[0]:
=false;
end;
processP1
begin
inside[1]:
=true;
whileinside[0]doifturn=0then
begin
inside[1]:
=false;
whileturn=0dobeginend;
inside[1]:
=true;
end
临界区;
turn=0;
inside[1]:
=false;
end;
coend
这种方法显然能保证互斥进入临界区的要求,这是因为仅当tum=i(i=0,1)时进程Pi(i=0,1)才能进入其临界区。
因此,一次只有一个进程能进入临界区,且在一个进程退出临界区之前,turn的值是不会改变的,保证不会有另一个进程进入相关临界区。
同时,因为turn的值不是0就是1,故不可能同时出现两个进程均在while语句中等待而进不了临界区,但是不能解决“空闲让进”问题。
3.2.1.2 Peterson算法
在1981年,G.L.Peteron提出了一个简单得多的软件互斥算法来解决互斥进入临界区的问题。
此方法为每个进程设置一个标志,当标志为true时表示该进程要求进入临界区。
另外再设置一个指针turn以指示可以由哪个进程进入临界区,当turn=i时则可由进程Pi进入临界区。
Petrson算法的程序如下:
varinside:
array[1..2]ofboolean;
turn:
integer;
turn:
=1or2;
inside[1]:
=false;/*P1不在其临界区内*/
inside[2]:
=false;/*P2不在其临界区内*/
cobegin
processP1
begin
inside[1]:
=true;
turn:
=2;
while(inside[2]andturn=2)
dobeginend;
临界区;
inside[1]:
=false;
end;
processP2
begin
inside[2]:
=true;
turn:
=1;
while(inside[1]andturn=1)
dobeginend;
临界区;
inside[2]:
=false;
end;
coend
在上面的程序中,用对turn的置值和while语句来限制每次最多只有一个进程可以进入临界区,当有进程在临界区执行时不会有另一个进程闯入临界区;进程执行完临界区程序后,修改insidei的状态而使等待进入临界区的进程可在有限的时间内进入临界区。
所以,Peterson算法满足了对临界区管理的三个条件。
由于在while语句中的判别条件是“insidei和turni”,因此,任意一个进程进入临界区的条件是对方不在临界区或对方不请求进入临界区。
于是,任何一个进程均可以多次进入临界区,克服了Dekker算法必须交替进入临界区的限制。
3.2.2硬件方法
分析临界区管理的尝试中的两种算法,问题出在管理临界区的标志时要用到两条指令,而这两条指令在执行过程中有可能被中断,从而导致了执行的不正确。
能否把标志看作为一个锁,开始时锁是打开的,在一个进程进入临界区时便把锁锁上以封锁其它进程进入临界区,直至它离开其临界区,再把锁打开以允许其它进程进入临界区。
如果希望进入其临界区的一个进程发现锁未开,它将等待,直到锁被打开。
可见,要进入临界区的每个进程必须首先测试锁是否打开,如果是打开的则应立即把它锁上,以排斥其它进程进入临界区。
显然,测试和上锁这两个动作不能分开,以防两个或多个进程同时测试到允许进入临界区的状态。
下面是一些硬件设施,可用来实现对临界区的管理。
3.2.2.1 关中断
实现这种管理的方法之一是关中断。
当进入锁测试之前关闭中断,直到完成锁测试并上锁之后再开中断。
在这一短暂的期间,计算机系统不响应中断,因此不会转向调度,也就不会引起进程或线程切换,从而保证了锁测试和上锁操作的连续性和完整性,有效的实现了临界区管理。
但关中断时间过长会影响系统效率,关中断方法也不适用于多CPU系统。
3.2.2.2 硬件指令方法
实现这种管理的另一种办法是使用硬件提供的“测试并建立”指令TS(TestandSet)。
可把这条指令看作为函数过程,它有一个布尔参数x和一个返回条件码,当TS(x)测到x为true时则置x为false,且根据测试到的x值形成条件码。
下面给出了TS指令的处理过程。
TS(x):
若x=true,则x:
=false;returntrue;否则returnfalse;
用TS指令管理临界区时,我们把一个临界区与一个布尔参数s相连,s初值置为true,表示没有进程在临界区内。
在进入临界区之前,我们首先用TS指令测试s,如果没有进程在临界区内,则可以进入,否则必须循环测试直到TS(s)为true,此时s的值一定为false;当进程退出临界区时,把s置为true。
由于TS指令是一个不可分指令,所以在测试和形成条件码之间不可能有另一进程去测试x值,从而保证临界区管理的正确性。
s:
boolean;
s:
=true;
processPi/*i=1,2,…,n*/
pi:
boolean;
begin
repeatpi:
=TS(s)untilpi;
临界区;
s:
=true;
end;
对换(swap)指令的功能是交换两个字的内容,处理过程描述如下:
swap(a,b):
temp:
=a;a:
=b;b:
=temp;
在80x86中,对换指令称为XCHG指令。
用对换指令可以简单有效地实现互斥,方法是为每个临界区设置一个布尔变量,例如称为lock,当其值为false时表示临界区未被使用,实现进程互斥的程序如下:
lock:
boolean;
lock:
=false;
processPi/*i=1,2,…,n*/
pi:
boolean;
begin
pi:
=true;
repeatswap(lock,pi)untilpi=false;
临界区;
lock:
=false;
end;
3.2.2.3 锁机制
锁机制是解决互斥问题的一种方案,在锁机制中通过原语保证资源状态的检查和修改作为一个整体来执行,从而能正确的实现互斥。
锁是一个代表资源状态的变量,通常用0表示资源可用(开锁),用1表示资源已被占用(关锁)。
进程在使用临界资源之前需要先考察锁变量的值,如果值为0则将锁设置为1(关锁),如果值为1则回到第一步重新考察变量的值。
当进程使用完资源后,应将锁设置为0。
3.3信号量与PV操作
3.3.1同步和同步机制
下面通过例子来进一步阐明进程同步的概念。
著名的生产者--消费者(Producer-ConsumerProblem)问题是计算机操作系统中并发进程内在关系的一种抽象,是典型的进程同步问题。
在操作系统中,生产者进程可以是计算进程、发送进程;而消费者进程可以是打印进程、接收进程等等。
解决好了生产者--消费者问题就解决好了一类并发进程的同步问题。
生产者--消费者问题表述如下:
有n个生产者和m个消费者,连接在一个有k个单位缓冲区的有界缓冲上,故又叫有界缓冲问题。
其中,pi和cj都是并发进程,只要缓冲区未满,生产者pi生产的产品就可投入缓冲区;类似地,只要缓冲区不空,消费者进程cj就可从缓冲区取走并消耗产品。
可以把生产者-消费者问题的算法描述如下:
vark:
integer;
typeitem:
any;
buffer:
array[0..k-1]ofitem;
in,out:
integer:
=0;
coumter:
integer:
=0;
processproducer
while(TRUE)/*无限循环
produceaniteminnextp;/*生产一个产品
if(counter==k)sleep();/*缓冲满时,生产者睡眠
buffer[in]:
=nextp;/*将一个产品放入缓冲区
in:
=(in+1)modk;/*指针推进
counter:
=counter+1;/*缓冲内产品数加1
if(counter==1)wakeup(consumer);/*缓冲为空了,加进一件产品
并唤醒消费者
processconsumer
while(TRUE)/*无限循环
if(counter==0)sleep();/*缓冲区空,消费者睡眠
nextc:
=buffer[out];/*取一个产品到nextc
out:
=(out+1)modk;/*指针推进
counter:
=counter-1;/*取走一个产品,计数减1
if(counter==k-1)wakeup(producer);/*缓冲满了,取走一件产品
并唤醒生产者
consumethriteminnextc;/*消耗产品
其中,假如一般的高级语言都有sleep()和wakeup()这样的系统调用。
从上面的程序可以看出,算法是正确的,两进程顺序执行结果也正确。
但若并发执行,就会出现错误结果,出错的根子在于进程之间共享了变量counter,对counter的访问未加限制。
生产者和消费者进程对counter的交替执行会使其结果不唯一。
例如,counter当前值为8,如果生产者生产了一件产品,投入缓冲区,拟做conuter加1操作。
同时消费者获取一个产品消费,拟做conuter减1操作。
假如两者交替执行加或减1操作,取决于它们的进行速度,counter的值可能是9,也可能是7,正确值应为8。
更为严重的是生产者和消费者进程的交替执行会导致进程永远等待,造成系统死锁。
假定消费者读取conuter发现它为0。
此时调度程序暂停消费者让生产者运行,生产者加入一个产品,将counter加1,现在counter等于1了。
它想当然地推想由于counter刚刚为0,所以,此时消费者一定在睡眠,于是生产者调用wakeup来唤醒消费者。
不幸的是,消费者还未去睡觉,唤醒信号被丢失掉。
当消费者下次运行时,因已测到counter为0,于是去睡眠。
这样生产者迟早会添满缓冲区,然后去睡觉,形成了进程都永远处于睡眠状态。
出现不正确结果不是因为并发进程共享了缓冲区,而是因为它们访问缓冲区的速率不匹配,或者说pi,cj的相对速度不协调,需要调整并发进程的进行速度。
并发进程间的这种制约关系称进程同步,交往的并发进程之间通过交换信号或消息来达到调整相互速率,保证进程协调运行的目的。
操作系统实现进程同步的机制称同步机制,它通常由同步原语组成。
不同的同步机制采用不同的同步方法,迄今己设计出许多种同步机制,本书中将介绍几种最常用的同步机制:
信号量及PV,管程和消息传递。
3.3.2记录型信号量与PV操作
前一节介绍的种种方法虽能保证互斥,可正确解决临界区调度问题,但有明显缺点。
对不能进入临界区的进程,采用忙式等待(busywaiting)测试法,浪费CPU时间。
将测试能否进入临界区的责任推给各个竞争的进程会削弱系统的可靠性,加重了用户编程负担。
1965年荷兰的计算机科学家E.W.Dijkstra提出了新的同步工具——信号量和P、V操作。
他将交通管制中多种颜色的信号灯管理交通的方法引入操作系统,让两个或多个进程通过信号量(Semaphore)展开交互。
进程在某一特殊点上停止执行直到得到一个对应的信号量,通过信号量这一设施,任何复杂的进程交互要求可得到满足,这种特殊的变量就是信号量。
在操作系统中,信号量用以表示物理资源的实体,它是一个与队列有关的整型变量。
实现时,信号量是一种变量类型,常常用一个记录型数据结构表示,它有两个分项:
一个是信号量的值,另一个是信号量队列的队列指针。
信号量仅能由同步原语对其进行操作,原语是操作系统中执行时不可中断的过程、即原子操作(AtomicAction)。
Dijkstra发明了两个同步原语:
P操作和V操作(荷兰语中“测试(Proberen)”和“增量(Verhogen)”的头字母,此外还用的符号有:
wait和signal;up和down;sleep和wakeup等。
本书中采用Dijkstra最早论文中使用的符号P和V。
利用信号量和P、V操作既可以解决并发进程的竞争问题,又可以解决并发进程的协作问题。
信号量按其用途可分为两种:
●公用信号量:
联系一组并发进程,相关的进程均可在此信号量上执行P和V操作。
初值常常为1,用于实现进程互斥。
●私有信号量:
联系一组