操作系统精髓与设计原理第5章 并发性互斥和同步Word文档下载推荐.docx
《操作系统精髓与设计原理第5章 并发性互斥和同步Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《操作系统精髓与设计原理第5章 并发性互斥和同步Word文档下载推荐.docx(15页珍藏版)》请在冰豆网上搜索。
二元信号量只能取0或1,而一般信号量可以取任何整数。
5.10强信号量与弱信号量有什么区别。
强信号量要求在信号量上等待的进程按照先进先出的规则从队列中移出。
弱信号量没有此规则。
5.11.什么是管程。
管程是由一个或多个过程,一个初始化序列和局部数据组成的软件模块。
5.12对于消息,有阻塞和无阻塞有什么区别?
5.13.通常与读者-写者问题相关联的有哪些条件?
1.任意多的读进程可以同时读这个文件,2.一次只有一个写进程可以往文件中写,3.如果一个写进程正在往文件中写时,则禁止任何读进程读文件。
习题:
5.1
b.协同程序read读卡片,将字符赋给一个只有一个字大小的缓冲区rs然后在赋给squash协同程。
协同程序Read在每副卡片图像的后面插入一个额外的空白。
协同程序squash不需要知道任何关于输入的八十个字符的结构,它简单的查找成对出现的星号,然后将更改够的字符串经由只有一个字符大小的缓冲sp,传递给协同程序print。
最后协同程序print简单的接受到来的字符串,并将他们打印在包含125个字符的行中。
5.2.考虑一个并发程序,它有两个进程p和q,定义如下。
A.B.C.D和E是任意的原子语句。
假设住程序执行两个进程的parbegin
Voidp()voidq()
{A;
{D;
B;
E;
C;
}
ABCDE;
ABDCE;
ABDEC;
ADBCE;
ADBEC;
ADEBC;
DEABC;
DAEBC;
DABEC;
DABCE;
5.3考虑下面的程序
constintn=50;
inttally;
voidtotal()
{intcount;
for(count=1;
count<
=n;
count++)
{tally++;
}
voidmain()
{
tally=0;
parbegin(total(),total();
write(tally);
a.随意一看,tally值的范围好像是落在[50,100]这个区间里,因为当没有互斥时可以从0直接增加到50.这一基本论点是当并发的运行这两进程时,我们不可能得到一个比连续执行单一某进程所得tally值还低的一个最终tally值.但是考虑下面由这两进程按交替顺序执行载入,增加,存储的情况,同时变更这个共享变量的取值:
1.进程A载入tally值,tally值加到1,在此时失去处理器(它已经增加寄存器的值到1,但是还没有存储这个值).
2.进程B载入tally值(仍然是0),然后运行完成49次增加操作,在它已经将49这个值存储给共享变量tally后,失去处理器控制权.
3.进程A重新获得处理器控制权去完成它的第一次存储操作(用1去代替先前的49这个tally值),此时被迫立即放弃处理器.
4.进程B重新开始,将1(当前的tally值)载入到它自己的寄存器中,但此时被迫放弃处理器(注意这是B的最后一次载入).
5.进程A被重新安排开始,但这次没有被中断,直到运行完成它剩余的49次载入,增加和存储操作,结果是此时tally值已经是50.
6.进程B在它终止前完成仅有的最后一次增加和存储操作.它的寄存器值增至2,同时存储这个值做为这个共享变量的最终结果.
一些认为会出现低于2这个值的结果,这种情况不会出现.这样tally值的正确范围是[2,100].
b.对一般有N个进程的情况下,tally值的最终范围是[2,N*50],因为对其他所有进程来说,从最初开始运行到在第五步完成.但最后都被进程B破坏掉它们的最终结果.
5.4.忙等待是否总是比阻塞等待效率低(根据处理器的使用时间)?
请解释。
就一般情况来说是对的,因为忙等待消耗无用的指令周期.然而,有一种特殊情况,当进程执行到程序的某一点处,在此处要等待直到条件满足,而正好条件已满足,此时忙等待会立即有结果,然而阻塞等待会消耗操作系统资源在换出与换入进程上.
5.5考虑下面的程序
booleanblocked[2];
intrurn;
voidP(intid)
{
While(true)
While(turn!
=id);
While(blocked[1-!
id]
/*donothing*/;
Turn=id;
Voidmain()
{
Blocked[0]=false;
Blocked[1]=false;
Turn=0;
Parbegin(P(0),P
(1));
这是【HYMA66】中提出的解决互斥问题的一种方法。
请举出证明该方法不正确的一个反例。
考虑这种情况:
此时turn=0,进程P
(1)使布尔变量blocked[1]的值为true,在这时发现布尔变量blocked[0]的值为false,然后P(0)会将true值赋予blocked[0]
此时turn=0,P(0)进入临界区,P
(1)在将1赋值给turn后,也进入了临界区.
5.6解决互斥的另一种软件方法是lamport的面包店(bakery)算法,之所以起这个名字,是因为它的思想来自于面包店或其他商店中,每个顾客在到达时都得到一个有编号的票,并按票号依次得到服务,算法如下:
Booleanchoosing[n];
Intnumber[n];
Choosing[i]=true;
Number[i]=1+getmax(number[],n);
Choosing[i]=false;
For(intj=0;
j<
n;
j++)
While(choosing[j])
{}
While((number[j]!
=0)&
&
(number[j],j)<
(number[i],i)
/*criticalsection*/
Number[i]=0;
/*remainder*/;
数组choosing和number分别被初始化成false和0,每个数组的第i个元素可以由进程i读或写,但其他进程只能读。
符号(a,b)<
(c,d)被定义成
(a,c)或(a=c且b<
d)
A.用文字描述这个算法。
B.说明这个算法避免了死锁。
C.说明它实施了互斥。
a.当一个进程希望进入临界区时,它被分配一个票号.分配的票号是通过在目前那些等待进入临界区的进程所持票号和已经在临界区的进程所持票号比较,所得最大票号再加1得到的.有最小票号的进程有最高的优先级进入临界区.当有多个进程拥有同样的票号时,拥有最小数字号进入临界区.当一个进程退出临界区时,重新设置它的票号为0.
b.如果每个进程被分配唯一的一个进程号,那么总会有一个唯一的,严格的进程顺序.因此,死锁可以避免.
c.为了说明互斥,我们首先需要证明下面的定理:
如果Pi在它的临界区,Pk已经计算出来它的number[k],并试图进入临界区,此时就有下面的关系式:
(number[i],i)<
(number[k],k).为证明定理,定义下面一些时间量:
Tw1:
Pi最后一次读choosing[k],当j=k,在它的第一次等待时,因此我们在Tw1处有choosing[k]=false.
Tw2:
Pi开始它的最后执行,当j=k,在它的第二次while循环时,因此我们有Tw1<
Tw2.
Tk1:
Pk在开始repeat循环时;
Tk2:
Pk完成number[k]的计算;
Tk3:
Pk设置choosing[k]为false时.我们有Tk1<
Tk2<
Tk3.
因为在Tw1处,choosing[k]=false,我们要么有Tw1<
Tk1,要么有Tk3<
Tw1.在第一种情况中,我们有number[i]<
number[k],因为Pi在Pk之前被分配号码;
这个满足定理条件.在第二种情况中,我们有Tk2<
Tk3<
Tw1<
Tw2,因此有Tk2<
Tw2.这意味着在Tw2时,Pi已经读了当前number[k]的值.而且,因为Tw2是当j=k第二次while循环执行发生的时刻,我们有(number[i],i)<
(number[k],k),这样完成了定理的证明.现在就很容易说明实施了互斥.假定Pi在临界区,Pk正试图进入临界区.Pk将不能进入临界区,因为它会发现number[i]不等于0,并且(number[i],i)<
(number[k],k).
5.7当按图5.2的形式使用一个专门机器指令提供互斥时,对进程在允许访问临界区之前必须等待多久没有控制。
设计一个使用testset指令的算法,且保证任何一个等待进入临界区的进程在n-1个turn内进入,n是要求访问临界区的进程数,turn是指一个进程离开临界区而另一个进程获准访问这个一个事件。
以下的程序由[SILB98]提供:
varj:
0..n-1;
key:
boolean;
repeat
waiting[i]:
=true;
key:
whilewaiting[i]andkeydokey:
=testset(lock);
=false;
<
criticalsection>
j:
=i+1modn;
while(j≠i)and(notwaiting[j])doj:
=j+1modn;
ifj=ithenlock:
=false
elsewaiting:
remaindersection>
Until
这个算法用最普通的数据结构:
varwaiting:
array[0..n–1]ofboolean
Lock:
boolean
这些数据结构被初始化成假的,当一个进程离开它的临界区,它就搜索waiting的循环队列
5.8考虑下面关于信号量的定义:
VoidsemWait(s)
If(s.count>
0)
s.count--;
Else
Placethisprocessins.queue;
Block;
VoidsemSignal(s)
If(thereisatliastoneprocessblockedonsemaphore)
RemoveaprocessPfroms.queue;
PlaceprocessPonreadylist;
Else
s.count++;
比较这个定义和图5.3中的定义,注意有这样的一个区别:
在前面的定义中,信号量永远不会取负值。
当在程序中分别使用这两种定义时,其效果有什么不同?
也就是说,是否可以在不改变程序意义的前提下,用一个定义代替另一个?
这两个定义是等价的,在图5.3的定义中,当信号量的值为负值时,它的值代表了有多少个进程在等待;
在此题中的定义中,虽然你没有关于这方面的信息,但是这两个版本的函数是一样的。
5.9可以用二元信号量实现一般信号量。
我们使用semWaitB操作和semSignalB操作以及两个二元信号量delay和mutex。
考虑下面的代码
VoidsemWait(semaphors)
semWaitB(mutex);
s--;
if(s<
semSignalB(mutex);
semWaitB(delay);
Semsignalb(mutex)
VoidsemSignal(semaphores);
s++;
if(s<
=0)
semSignalB(delay);
最初。
S被设置成期待的信号量值,每个semwait操作将信号量减1,每个semsignal操作将信号量加1.二元信号量mutex被初始化成1,确保在更新在更新s时保证互斥,二元信号量delay被初始化成0,用于挂起进程,
上面的程序有一个缺点,证明这个缺点,并提出解决方案。
提示:
假设两个进程,每个都在s初始化为0时调用semwait(s),当第一个刚刚执行了semsignalb(mutex)但还没有执行semwaitb(delay),第二个调用semwait(s)并到达同一点。
现在需要做的就是移动程序的一行.
假设两个进程,每个都在s被初始化成0时调用semWait(s),当第一个刚执行了semSignalB(mutex)但还没有执行semWaitB(delay)时,第二个调用semWait(s)并到达同一点。
因为s=-2mutex没有锁定,假如有另外两个进程同时成功的调用semSignal(s),他们接着就会调用semsignalb(delay),但是第二个semsignalb没有被定义。
解决方法就是移动semWait程序中end前的else一行到semSignal程序中最后一行之前。
因此semWait中的最后一个semSignalB(mutex)变成无条件的,semSignal中的semSignalb(mutex)变成了有条件的。
5.101978年,dijkstra提出了一个推测,即使用有限数目的弱信号量,没有一种解决互斥的方案,使用于数目未知但有限的进程且可以避免饥饿。
1979年,j.m.morris提出了一个使用三个弱信号量的算法,反驳了这个推测。
算法的行为可描述如下,如果一个或多个进程正在semwait(s)操作上等待,另一个进程正在执行semsignal(s),则信号量s的值未被修改,一个等待进程被解除阻塞,并且这并不取决于semwait(s)。
除了这三个信号量外,算法使用两个非负整数变量,作为在算法特定区域的进程的计数器。
因此,信号量A和B被初始化为1,而信号量M和计数器NA,NM被初始化成0.一个试图进入临界区的进程必须通过两个分别由信号量A和M表示路障,计数器NA和NM分别含有准备通过路障A以及通过路障A但还没有通过路障M的进程数。
在协议的第二部分,在M上阻塞的NM个进程将使用类似于第一部分的串联技术,依次进入他们的临界区,定义一个算法实现上面的描述。
这个程序由[RAYN86]提供:
vara,b,m:
semaphore;
na,nm:
0…+∞;
a:
=1;
b:
m:
=0;
na:
nm:
semWait(b);
na←na+1;
semSignal(b);
semWait(a);
nm←nm+1;
semwait(b);
na←na–1;
ifna=0thensemSignal(b);
semSignal(m)
elsesemSignal(b);
semSignal(a)
endif;
semWait(m);
nm←nm–1;
criticalsection>
;
ifnm=0thensemSignal(a)
elsesemSignal(m)
5.11下面的问题曾被用于一个测试中:
侏罗纪公园有一个恐龙博物馆和一个公园,有m个旅客和n辆车,每辆车只能容纳一名旅客。
旅客在博物馆逛了一会儿,然后派对乘坐旅客车。
当一辆车可用时,它载入一名旅客,然后绕公园行驶任意长的时间。
如果n辆车都已被旅客乘坐游玩,则想坐车的旅客需要等待;
如果一辆车已经就绪,但没有旅客等待,那么这辆车等待。
使用信号量同步m个旅客进程和n个进程。
下面的代码框架是在教室的地板上发现的。
忽略语法错误和丢掉的变量声明,请判定它是否正确。
注意,p和v分别对应于semwait和semsignal。
ResourceJurassic_Park()
Semcar_avail:
=0,car_taken:
=0,car_fillde:
=0.passenger_released:
=0
Processpassenger(i:
=1tonum_passengers)
Dotrue->
nap(int(random(1000*wander_time)))
P(caravail);
V(car_taken);
P(car_filled)
P(passenger_released)
Od
Endpassenger
Processcar(j:
=1tonum_cars)
V(car_avail);
P(car_taken);
V(car_filled)
Nap(int(random(1000*ride_time)))
V(passenger_released)
Od
Endcar
EndJurassic_Park
这段代码有一个重要问题.在processcar中的代码V(passenger_released)能够解除下面一种旅客的阻塞,被阻塞在P(passenger_released)的这种旅客不是坐在执行V()的车里的旅客.
5.12在图5.9和5.3的注释中,有一句话是“仅把消费者临界区(由s控制)中的控制语句移出还是不能解决问题,因为这将导致死锁”,请用类似于表5.3的表说明。
Producer
Consumer
s
n
delay
1
2
SemWaitB(S)
3
n++
4
If(n==1)
(semSignalB(delay))
5
semSignalB(s)
6
semWaitB(delay)
7
semWaitB(s)
8
n--
9
If(n==0)(semWaitB(delay))
10
生产者和消费者都被阻塞。
5.13考虑图5.10中定义的无限缓冲区生产者/消费者问题的解决方案。
假设生产者和消费者都以大致相同的速度运行,运行情况如下:
生产者:
append;
semSignal;
produce;
·
semSignal
消费者:
consume;
take;
semWait;
生产者通常管理给换成区一个元素,并在消费者消费了前面的元素后发信号。
生产者通常添加到一个空缓冲去中,而消费者通常取走缓冲区中的唯一元素。
尽管消费者从不在信号量上阻塞,但必须进行大量的信号量调用,从而产生相当多的开销。
构造一个新程序使得能在这种情况下更加有效。
允许n的值为-1,这表示不仅缓冲区为空,而且消费者也检测到这个事实并将被阻塞,直到生产者产生新数据。
这个方案不需要使用图5.10中的局部变量m。
这个程序来自于[BEN82]
programproducerconsumer;
varn:
integer;
s:
(*binary*)semaphore(:
=1);
delay:
=0);
procedureproducer;
begin
semWaitB(s);
n:
=n+1;
ifn=0thensemSignalB(delay);
forever
end;
procedureconsumer;
=n–1;
ifn=-1then
semSignalB(s);
semWaitB(delay);
begin(*mainprogram*)
parbegin
producer;
consumer
parend
end.
5.14考虑图5.13.如果发生下面的交换,程序的意义是否会发生改变?
a.semWait(e);
semWait(s)
b.semSignal(s);
semSignal(n)
c.semWait(n);
d.semSignal(s);
semSignal(e)
只要交换顺序都会导致程序错误。
信号量s控制进入临界区,你只想让临界区区域包括附加或采取功能。
5.15在讨论有限缓冲区(见图5.12)生产者/消费者问题时,注意我们的定义允许缓冲区中最多有n-1个入口?
a.这是为什么?
b.请修改程序,以不久这种低调?
如果缓冲区可以容纳n个入口,问题在于如何从一个满