第4讲 进程管理之进程同步.docx
《第4讲 进程管理之进程同步.docx》由会员分享,可在线阅读,更多相关《第4讲 进程管理之进程同步.docx(24页珍藏版)》请在冰豆网上搜索。
![第4讲 进程管理之进程同步.docx](https://file1.bdocx.com/fileroot1/2023-1/4/7b55db71-45a6-467d-a8cd-b6c240dffb8b/7b55db71-45a6-467d-a8cd-b6c240dffb8b1.gif)
第4讲进程管理之进程同步
第四讲进程管理之进程同步
一、为什么要引入进程同步?
进程同步有什么作用?
在引入了进程后,提高了资源的利用率。
但是由于有限的资源也导致进程之间的资源竞争和共享。
现在我们来看在进程的并发执行过程中存在的制约。
1、两种形式的制约关系
A.间接制约-由于共享某一公有资源而引起的在临界区内不允许并发进程交叉执行的现象。
如两个进程都要使用打印机,则只有一个进程能获得,另一个进程只有等待。
只有当打印机被释放后,该进程才能执行。
B.直接制约-简单说就是一组异步环境下的并发进程,各自的执行结果互为对方的执行条件,从而限制各进程的执行速度的过程。
进程同步的任务也就是其作用是使并发执行的诸进程之间能有效共享资源和相互合作,从而使程序的执行有可再现性。
也就是能使诸进程能够顺利执行下去。
从这里我们可以看出,进程同步只有解决了进程间的间接制约和直接制约关系,才能使进程顺利协调执行。
解决A的方法是:
保证诸进程互斥访问临界资源。
即进程互斥
解决B的方法是:
协调相互合作的诸进程的执行次序,狭义的同步。
即进程同步
那么现在明白了,进程同步包括,解决间接制约的进程互斥和解决直接制约的进程同步。
二、进程同步的基本概念
一)临界资源
Ø概念:
一段时间内只允许一个进程访问的资源。
也就是竞争的那个公有资源。
Ø要求:
共享临界资源的诸进程必须互斥访问临界资源。
Ø举例:
生产者-消费者问题
生产者()消费者()
…….
放产品取产品
Counter:
=counter+1Counter:
=Counter-1
….….
机器语言形式为:
现在我们来看结果,由于生产者和消费者并发执行,并共享Counter,因此有:
假设Counter的当前值是5,正确的结果应该是5。
这表明:
程序的执行不具有可再现性。
为了解决此错误,应把变量Counter作为临界资源处理。
也就是要生产者进程和消费者进程互斥的访问变量Counter.
Ø结论:
作为临界资源,生产者进程和消费者进程应该互斥访问。
也就是,虽然生产者进程和消费者进程并发执行,但在执行Counter加1,减1时,要顺序执行。
也就是只能安上述A或B的顺序执行。
不能交替。
Ø临界资源实例:
硬件中的打印机、磁带机。
软件中的变量、队列、缓冲区等。
二)临界区(criticalsection)
1、概念:
不允许多个并发进程交叉执行的一段程序就称为临界区,也就是每个进程中访问临界资源的那段代码。
例如:
生产者的Counter:
=counter+1,消费者的Counter:
=Counter-1都是CS
2、临界区的进入:
要互斥的进入临界区,就可实现对临界资源的互斥访问。
要做到这点,必须在进入临界区之前对欲访问的临界资源进行检查,若此刻临界资源未被访问,则该进程进入临界区,对该资源访问,并设置它正被访问的标志;否则,该进程不能进入临界区。
因此描述为如下:
v进入区—增加在临界区前面的一段代码,用于检查欲访问的临界资源此刻是否被访问。
v退出区—增加在临界区后面的一段代码,用于将临界资源的访问标志恢复为未被访问标志。
v剩余区—进程中除了进入区、临界区及退出区之外的其余代码。
要进入临界区的若干进程必须满足:
同学们看一下。
加深理解。
(1)一次只允许一个进程进入临界区
(2)任何时候,处于临界区的进程不得多于一个
(3)进入临界区的进程要在有限的时间内退出
(4)如果不能进入自己的临界区,则应让出处理机资源
3、同步机制准则:
看课本说一下
空闲让进
当无进程处于临界区内时,必须让一个要求进入临界区的进程立即进入,以有
效地利用临界资源。
忙则等待
当已有进程处于临界区内时,其它试图进入临界区的进程必须等待,以保证它
们互斥地进入临界区。
有限等待
对要求进入临界区的进程,应在有限时间内使之进入,以免陷入“死等”。
让权等待
对于等待进入临界区的进程而言,它必须立即释放处理机,以免进程“忙等”
三、信号量机制semaphore
在解决临界区问题上,有些课本还提到了软件方法和硬件方法,有兴趣的同学自己下去看
信号量和P、V原语概念都是由荷兰科学家Dijkstra提出来的。
我们用信号量及PV操作来实现进程的同步和互斥。
PV操作属于进程的低级通信。
一)信号量概念
1、整型信号量
⏹整型信号量——非负整数,除了初始化外,只能通过两个原子操作wait和signal(P,V)来访问。
⏹wait和signal操作描述:
wait(S):
whileS0dono-op测试有无可用资源no-op是无操作。
S:
=S-1;可用资源数减一个单位
signal(S):
S:
=S+1;
⏹主要问题:
只要S0,wait操作就不断地测试(盲等),因而,未做到“让权等待”。
2、记录型信号量
由于采用了记录型结构而得名。
⏹基本思想
1、设置一个代表资源数目的整型变量value(资源信号量)
2、设置一链表L用于链接所有等待的进程
⏹记录型信号量的数据结构
Typesemaphore=record
value:
integer;
L:
listofprocess;
end
⏹wait和signal操作描述:
wait(S):
S.value:
=S.value-1;
ifS.value<0thenblock(S.L);
signal(S):
S.value:
=S.value+1;
ifS.value0thenwakeup(S.L);
做到“让权等待”。
3、AND型信号量和一般信号量集了解一下就可以
⏹AND型信号量的基本思想
将进程在整个运行过程中所需要的所有临界资源,一次性全部分配给进程,待进程使用完后再一起释放。
只要有一个资源未能分配给进程,其它所有可能分配的资源也不分配给该进程。
从而可避免死锁发生。
在wait操作中,增加了一个“AND”条件,故称为AND同步。
⏹一般信号集的基本思想
一次可分配多个某种临界资源,而不需执行N次的P操作;每次分配前都测试该种资源数目是否大于测试值,低于此值,不分配。
总结和补充:
注意:
用于互斥的信号量初值应该大于0。
信号量的值仅能由PV操作来改变。
这是因为进程互斥是由资源共享造成的间接制约产生的,因此信号量初值大于0,这样说明出初始时有资源。
二)P、V原语
1、什么是P、V原语?
P操作和V操作是不可中断的程序段,称为原语。
P,V原语中P是荷兰语的Passeren,相当于英文的pass, V是荷兰语的Verhoog,相当于英文中的incremnet。
一般来说,信号量S0时,S表示可用资源的数量。
执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。
而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
ØP原语操作的动作是:
指的是记录型信号量
(1) sem减1;
(2) 若sem减1后仍大于或等于零,则进程继续执行;
(3) 若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
ØV原语操作的动作是:
指的是记录型信号量
(1) sem加1;
(2) 若相加结果大于零,则进程继续执行;
(3) 若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
注意:
就是P,V操作对于每一个进程来说,都只能进行一次。
而且必须成对使用。
且在P,V愿语执行期间不允许有中断的发生。
P原语图
V原语图
四、信号量机制的应用
一)利用信号量实现前趋关系
利用信号量描述程序或语句之间的前趋关系。
如:
设S1,S2,S3,S4为一组合作进程,其前趋图如图所示,用P、V操作实现其同步。
解:
说明:
比较好理解,每一个进程执行都要先以上一个进程为入口,并以唤醒下一个进程为出口。
看例子,课本也行
vara,b,c,d:
semaphore:
=0,0,0,0;/*表示进程是否执行完*/
main()
processS4()
{
P(c)
P(d)
……
…….
}
processS1()
{
……
…….
V(a)
}
processS3()
{
P(a)
P(b)
……
…….
V(d)
}
processS2()
{
……
…….
V(b)
V(c)
}
{
cobegin
s1();
s2();
s3();
s4();
coend
}
二)利用信号量和PV操作实现进程互斥的一般模型是:
1、信号量实现进程互斥原理:
由于用于互斥的信号量sem与所有的并发进程有关,所以称之为公有信号量。
公有信号量的值反映了公有资源的数量。
把临界区置于P(sem) 和V(sem)之间。
当一个进程想要进入临界区时,它必须先执行P原语操作以将信号量sem减1,在进程完成对临界区的操作后,它必须执行V原语操作以释放它所占用的临界区。
从而就实现了进程的互斥:
举个例子说明一下,只要把临界区置于P(sem)和V(sem)之间,即可实现进程间的互斥。
就象火车中的每节车厢只有一个卫生间,该车厢的所有旅客共享这个公有资源:
卫生间,所以旅客间必须互斥进入卫生间,只要把卫生间放在P(sem)和V(sem)之间,就可以到达互斥的效果。
2、一般模型
进程P1进程P2……进程Pn
………………
P(S);P(S);P(S);
临界区;临界区;临界区;
V(S);V(S);V(S);
……………………
其中信号量S用于互斥,初值为1。
3、PV操作实现进程互斥时应该注意的是:
(1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。
若有多个分支,要认真检查其成对性。
(2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。
为啥不能有死循环?
若临界区内有死循环,则共享资源就不会被释放。
始终被该进程占有。
同学们想一下。
(3)互斥信号量的初值一般为1。
具体情况具体分析,也有不为1的。
例1生产围棋的工人不小心把相等数量的黑子和白子混装载一个箱子里,现要用自动分拣系统把黑子和白子分开,该系统由两个并发执行的进程组成,功能如下:
(1)进程A专门拣黑子,进程B专门拣白子;
(2)每个进程每次只拣一个子,当一个进程在拣子时不允许另一个进程去拣子;
分析:
首先拿到这个题目,我们要明确的是我们要做什么?
其实,我们要实现或者说设计这个自动分拣器,也就是要实现题目中的两个功能。
那么我们怎么实现这样两个功能呢?
先做下面的分析:
第一步:
确定有哪几个进程和进程间的关系。
由于有共享资源,因此进程必须互斥的占有该资源。
由功能
(2)可知进程之间是互斥的关系。
第二步:
确定公有资源,设置信号量,判断临界区:
由于进程A和进程B要互斥进入箱子去拣棋子,无论进程A还是进程B为完成拣棋子这个事,需要得到两个资源实体,即箱子和棋子。
明明是两个资源实体啊,但是对于进程A来说,黑色棋子是其独占的。
因此棋子不是公有资源。
但是箱子是进程A和B都要用到的,因此箱子是两个进程的公有资源,确定了箱子为其公有资源,则设置一个信号量s。
从题目功能2中可以明显看出,临界区是拣棋子。
第三步:
确定信号量S的值。
其值取决于公有资源的资源实体的数目,由于箱子只有一个,s的初值就设为1。
实现算法:
展开就是一般模型的形式
begin
s:
semaphore;
s:
=1;
cobegin
processA
begin
L1:
P(s);
拣黑子;
V(s);//拣完黑子要释放资源
gotoL1;
end;
processB
begin
L2:
P(s);
拣白子;
V(s);
gotoL2;
end;
coend;
end;
说明:
好好理解。
1、进程间是否互斥,关键是看进程间是否共享某一公有资源,一个公有资源与一个信号量相对应。
确定信号量的值是一个关键点,它代表了可用资源实体数。
2、另外我们看这个解决方案结构,cobegin和coend括起来的语句表示其并发执行。
我们要理解这个系统的目的,它是用PV原语把临界区包起来,让它只能被互斥访问。
A,B进程是并发执行,但是在进入临界区时,用PV原语进行控制。
应该这样理解,这个算法不是等到A拣完,B才会拣,即A,B不是顺序执行。
A进程在执行的时候,B随时会来,这里所谓的来应该是中断。
因为PV原语不允许中断发生,当进程执行到临界区中时,因为资源已经被占用,则刚来的进程应该等待。
因此当进程执行到临界区中时,也不能被中断。
这也是互斥的目的。
(B来不来随机的,随时都可来,也可能连续拣了几个黑子,B都不来,也可能A,B进程是,你来一个我来一个,也就是会出现交替的拣黑子和白子的现象。
仔细理解)即A,B是并发执行。
要注意的一点是,当某个进程正在临界区执行时,其他进程如果执行了P原语,则该进程因为进不了临界区而进入等待队列中等待其他进程做V原语释放资源后,再进入临界区,这个时候,它的P原语执行才算真正结束。
例2某车站售票厅,任何时刻最多可容纳20名购票者进入,当售票厅中少于20名购票者时,厅外的购票者可立即进入,否则需要在外面等待。
每个购票者可看成一个进程。
分析:
第一步:
确定有哪几个进程和进程间的关系。
每个购票者就是一个进程。
售票厅是各进程共享的公有资源,当售票厅中多于20名购票者时,厅外的购票者需要在外面等待。
所以进程间是互斥的关系。
第二步:
确定公有资源,信号量及临界区。
只有一个公有资源:
售票厅,所以设置一个信号量s,临界区为进入售票厅,买票,退出售票厅。
第三步:
确定信号量的值。
售票厅最多容纳20个进程,即可供并发进程使用的资源实体数为20,s的初值就设为20。
一个公有资源,其资源实体数目是20个,也就是这个公有资源可容纳20个并发进程使用。
实现:
展开就是一般模型的形式
begin
s:
semaphore;
s:
=20;
cobegin
processPI(I=1,2,……n)//n个并发的进程。
beginP(s);
进入售票厅;
购票;
退出;
V(s);
end;
coend
说明:
当购票者进入售票厅前要执行P(s)操作,执行后若s大于或等于零,说明售票厅的人数还未满可进入。
执行后若s小于零,则说明售票厅的人数已满不能进入。
这个实现中同时最多允许20个进程进入售票厅购票,其余进程只能等待。
三)利用信号量和PV操作实现进程同步的一般模型
1、信号量实现进程同步原理:
与进程互斥不同,进程同步时的信号量只与制约进程及被制约进程有关而不是与整组并发进程有关,所以称该信号量为私有信号量。
PV操作是典型的同步机制之一。
私有信号量值:
用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生,也就是进程不能执行和前面的信号量定义不矛盾,因为如果为0,在P原语中减1,则变为小于0,进程自然不能继续执行;当信号量的值非0时,表示期望的消息已经存在,进程可以执行了。
用PV操作实现进程同步时,调用P操作测试消息是否到达,调用V操作发送消息。
2、PV原语实现进程同步的方法是:
首先判断进程间的关系为同步的,且为各并发进程设置私有信号量,然后为私有信号量赋初值,最后利用PV原语和私有信号量规定各进程的执行顺序。
这里大概说一下,下面针对例子详细讲。
3、使用PV操作实现进程同步时应该注意的是:
(1)分析进程间的制约关系,确定信号量种类。
在保持进程间有正确的同步关系情况下,哪个进程先执行,哪些进程后执行,彼此间通过什么资源(信号量)进行协调,从而明确要设置哪些信号量。
(2)信号量的初值与相应资源的数量有关,也与P、V操作在程序代码中出现的位置有关。
(3)同一信号量的P、V操作要成对出现,但它们分别在不同的进程代码中。
例3在例1的基础之上再添加一个功能:
(3)当一个进程拣了一个棋子(黑子或白子)以后,必让另一个进程拣一个棋子(黑子或白子)。
分析:
第一步:
确定有哪几个进程和进程间的关系。
由功能
(1)
(2)(3)可知,进程间的关系为同步关系。
第二步:
确定进程之间的操作的顺序:
首先:
先把各个进程的操作顺序画图。
这个题太简单,就一个操作拣黑子,拣白子。
其次:
确定信号量及其初值。
进程A和B共享箱子这个公有资源,但规定两个进程必须轮流去取不同色的棋子,因而相互间要互通消息。
用一个信号量表示进程A希望的消息,也就是当这个信号量的值非0时,进程A可以执行。
另一个信号量表示进程B希望的消息,即当这个信号量的值非0时,进程B能够执行。
因此需要两个私有信号量。
明确两个进程之间是哪个进程第一个执行?
明确这个也就确定了这个最先执行的进程的私有信号量初值是0还是非0。
此题比较简单。
明显看出,一种方案是先拣黑子,再拣白子,接着再拣黑子,轮流。
第二种可以是拣白子先执行,再拣黑子,轮流。
拣黑子和拣白子这两个操作是不能同时执行而是必须轮流的。
现在我们按第一方案来做,那么可以得到进程A的信号量初值非0(因为拣黑子要执行),进程B的信号量的初值一定是0,因为它不执行,必须在拣黑子完后执行。
那么非0到底值应该是多少,要和具体资源个数有关。
这两个私有信号量的值和资源个数有关。
现在我们来确定这道题的信号量的值,对于进程A可设置一个私有信号量s1,该私有信号量用于判断进程A是否能去拣黑子,因公有资源箱子就一个。
因此初值为1,表明。
对于进程B同样设置一个私有信号量s2,该私有信号量用于判断进程B是否能去拣白子,初值为0。
当然你也可以设置s1初值为0,s2初值为1(按第二方案做)。
再次:
PV原语位置和操作顺序:
P原语必须出现在受到另一个进程影响的那个操作之前;V原语必须出现在制约别的进程执行的那个操作之后。
processB
begin
L2:
P(s2);
拣白子;
V(s1);
gotoL2;
end;
实现:
begin
s1,s2:
semaphore;
s1:
=1;s2:
=0;
cobegin
processA
begin
L1:
P(s1);
拣黑子;
V(s2);
gotoL1;
end;
coend
注意:
另外一个问题就是P原语是不是一定在V原语的前面?
回答是否定的。
下面看一个例子。
这个题太简单,下面看个稍微难点的。
例4设在公共汽车上,司机和售票员的活动分别是:
司机:
启动车辆,正常行车,到站停车。
售票员:
上乘客,关车门,售票,开车门,下乘客。
用PV操作对其控制。
(华中理工大学1999年试题)
分析:
看看思维是渐进的,一定要认真分析。
第一步:
确定有哪几个进程和进程间的关系。
司机到站停车后,售票员方可工作。
同样,售票员关车门后,司机才能工作。
所以司机与售票员之间是一种同步关系。
第二步:
确定进程之间操作的顺序。
这个题复杂一些。
做这个分析最好画图。
首先:
先把各个进程的操作顺序画图。
从下面这个图中我们可以看出,关车门后,才能启动车辆;到站停车后,才能开车门;售票和正常行使可以同时进行。
从这个分析可以得出,
其次:
确定信号量及其初值。
售票员进程必须在司机进程前启动,因为只有关车门后才能启动车辆,从这点看出,司机进程的私有信号量初值必须为0;而司机进程到站停车决定了售票员进程能否执行,因此售票员进程这时又后于司机进程执行,因此售票员进程的初值是0。
再次:
PV原语位置和操作顺序:
V原语必须出现在制约别的进程执行的那个操作之后。
必须用原语控制在关完车门后,司机进程才能执行,也就是在关完车门后,用原语让司机进程的信号量值为非0,这样司机进程就可执行啦,因为司机进程的信号量初值是0,这里应该用V原语让其变为1;必须在司机进程到站停车后,用原语控制,才让售票员进程进行,也就是在司机进程到站后,原语让售票员进程值为非0。
这样售票员进程才可以执行,因为售票员进程信号量初值是0,因此这里需用V原语让其为1。
现在来看P原语位置,P原语必须出现在受到另一个进程影响的那个操作之前,因为在这个操作之前需要测试信号量的值,才能决定是否能执行这个操作。
因此可以看出,在司机进程的启动车辆之前要加P原语(因为启动车辆受到售票员进程关车门操作的制约),在售票员进程开车门之前要有P原语(因为开车门受到司机进程到站停车操作的制约)。
现在我们表述:
司机进程设置一个私有信号量run,用于判断司机能否进行工作,初值为0。
售票员进程设置一个私有信号量stop,用于判断是否停车,售票员是否能够开车门,初值为0。
实现:
beginstop,run:
semaphore
conductor:
begin
L2:
上乘客;
关车门;
V(run);
售票;
P(stop);
开车门;
下乘客;
gotoL2;
end;
coend;
end;
stop:
=0;run:
=0;
cobegin
driver:
begin
L1:
P(run);
启动车辆;
正常行车;
到站停车;
V(stop);
goto L1;
end;
用PV操作还可以实现进程同步与互斥的混合问题,典型的如:
多个生产者和多个消费者共享容量为n的缓存区。
这个例子在很多书中都有介绍,我们下一讲再讲。
总结:
一、进程同步和进程互斥的概念差别
1、进程同步和互斥的区别:
进程同步一定是进程A执行完,进程B执行。
这样轮流执行的。
而进程互斥不一定这样了。
有可能是进程A执行了几个来回,进程B才到来执行一次,或者进程A一直在执行,进程B始终没来执行;也可能很巧,也是进程A执行完,进程B执行,这样轮流,好像和进程同步一样。
同学们仔细分析例子1和例3就明白啦。
2、进程同步和互斥的判断:
那么我们怎么来做题呢?
首先我们看到进程同步比进程互斥的要求更强,条件更严格,因为它是严格要轮流执行的;而进程互斥可能在巧合时,是轮流执行的。
从这点考虑:
我们判断两个进程之间关系,如果是进程同步,那就不用再考虑他们之间的互斥(因为肯定满足互斥),这是个做此类题的原则;如果只有互斥,那就没啥。
下一讲我们来学习生产者和消费者问题,仔细分析它的第三例子,就会明白啦。
二、进程互斥和进程同步的分析过程总结:
第一步,我在这里详细讲。
第一步:
确定是进程同步还是进程互斥还是混合问题?
三个如果,就是原则
如果:
进程之间只有公有资源的竞争,就是进程互斥问题;如:
例1,例2。
如果:
同样的进程之间有公有资源的竞争也