操作系统复习笔记(一)

1.整型信号量是一个整数变量,除初始化外,对其只能执行两个操作,即wait(s)和signal(s),也叫p(s)和v(s)操作,均是原语操作,用来实现进程的同步,互斥.
2.记录型信号量

None.gif typesemaphore = record
None.gifvalue:integer
None.gifL:listofprocess;
None.gifend
None.gifprocedurewait(s)
None.gifvars:semaphore;
None.gifbegin
None.gifs.value
= s.value - 1 ;
None.gif
if (s.value < 0 )thenblock(s.L)
None.gifend
None.gifproceduresignal(s)
None.gifvars:semaphore
None.gifbegin
None.gifs.value
= s.value + 1 ;
None.gif
if (s.value <= 0 )thenwakeup(s.L)
None.gifend
None.gif


结论:
(1) 若信号量s为正值,则等于在封锁进程之前对信号梁可以施行的p操作数,也就是s所代表的实际可以使用的物理资源数.
(2) 若信号量s为负值,则其绝对值等于排列在该信号量s队列之中等待的进程个数,也就等于对信号量s实施p操作而被封锁起来并进入信号量s队列的进程数.
(3)p操作一般代表请求一个资源,v操作一般代表着释放一个资源,在一定条件下,p操作代表挂起进程操作,v操作代表唤醒被挂起的进程.
3.经典同步问题
1)生产者/消费者问题
a) 问题一:有一个生产者和一个消费者,共享一个缓冲区.两者要互斥访问缓冲区.
解:定义两个信号量:empty表示缓冲是否为空,初值为1,即初始时可以存入一件物品.full表示缓冲区中是否有物品,初值为0,即初始时缓冲区没有物品.

None.gif 程序:
None.gifbegin
None.gifbuffer:integer
None.gifempty,full:semaphore
= 1 , 0 ;
None.gifcobegin
None.gifprocessProducer
None.gifbegin
None.gifL1:生产一件物品
None.gifwait(empty);
None.gifbuffer
= product;
None.gifsignal(full);
None.gif
goto L1;
None.gifend
None.gifprocessConsumer
None.gifbegin
None.gifL2:wait(full);
None.gif从缓冲区取出一件物品;
None.gifsignal(empty);
None.gif消费掉物品
None.gif
goto L2;
None.gifend
None.gifcoend
None.gifend


注:
(1)进程互斥只需要一个信号量,而同步可能要两个信号量.
(2)p,v操作仍然要成对,但在进程进入临界区前后调用的是针对不同信号量的wait,signal操作,而进程互斥时是针对相同的信号量.
(3)至少有一个信号量的初值>=1,否则所有进程无法执行,一般是指管理是否允许访问共享资源的那个信号量.如这里的empty设为1,如果缓冲区容量为n,则可以设为n;

b)问题二:m个生产者,n个消费者,容量为r的缓冲区,(m,n,r都大于1),不要生产者和消费者互斥存取物品.
解:1)生产者和消费者之间要同步,类似问题一,用两个信号量empty,full;2)m个生产者之间要互斥,n个消费者之间也要互斥,但生产者和消费者不用互斥存取物品,因此设两个计数器in,out和相应的互斥信号量mutex1,mutex2

None.gif 程序:
None.gifbegin
None.gifbuffer:array[
0 dot.gifr - 1 ]:integer
None.gif
in , out :integer = 0 , 0 ;
None.gifempty
= r,full = 0 ,mutex1 = 1 ,mutex2 = 1 :semaphore;
None.gifcobegin
None.gifprocessProducer
- i(i = 1 , 2 dot.gifm)
None.gifbegin
None.gifL1:生产一件物品;
None.gifwait(empty)
None.gifwait(mutex1)
None.gifbuffer[
in ] = product;
None.gif
in = ( in + 1 )modr;
None.gifsignal(mutex1)
None.gifsignal(full)
None.gif
goto L1;
None.gifend
None.gifprocessConsumer
- j(j = 1 , 2 dot.gifn)
None.gifbegin
None.gifL2:wait(full)
None.gifwait(mutex2)
None.giftakeaproductfrombuffer[
out ];
None.gif
out = ( out + 1 )modr;
None.gifsignal(mutex2)
None.gifsignal(empty)
None.gif消费一件物品;
None.gif
goto L2;
None.gifend
None.gif


c)问题三:m个生产者,n个消费者,容量为r的缓冲区,(m,n,r都大于1),要求生产者和消费者互斥存取物品.
解:1)生产者和消费者之间要同步,类似问题一,用两个信号量empty,full;2)m个生产者之间要互斥,n个消费者之间也要互斥,同时要求生产者和消费者互斥存取物品,因此设两个计数器in,out和一个互斥信号量mutex.

代码:

None.gif begin
None.gifbuffer:array[
0 dot.gifr - 1 ]:integer
None.gif
in , out :integer = 0 , 0 ;
None.gifempty
= r,full = 0 ,mutex = 1 :semaphore;
None.gifcobegin
None.gifprocessProducer
- i(i = 1 , 2 dot.gifm)
None.gifbegin
None.gifL1:生产一件物品;
None.gifwait(empty)
None.gifwait(mutex)
None.gifbuffer[
in ] = product;
None.gif
in = ( in + 1 )modr;
None.gifsignal(mutex)
None.gifsignal(full)
None.gif
goto L1;
None.gifend
None.gifprocessConsumer
- j(j = 1 , 2 dot.gifn)
None.gifbegin
None.gifL2:wait(full)
None.gifwait(mutex)
None.giftakeaproductfrombuffer[
out ];
None.gif
out = ( out + 1 )modr;
None.gifsignal(mutex)
None.gifsignal(empty)
None.gif消费一件物品;
None.gif
goto L2;
None.gifend
None.gif

注:应该先执行对资源信号量的p,v操作,再执行互斥信号量的p,v操作,否则会引起死锁.

2)读者/写者问题
要求:(1)允许多个读者同时从数据区读数据;
(2)当读者在读数据时,不允许写者写数据
(3)任何时候只允许一个写者向数据区写数据;
(4)若有写者在写数据,则不允许读者读数据.
问题分为读者优先和写者优先两类。若有读者在读数据时,写者到来时就会被阻塞,直到所有读者读完数据才被唤醒.这里读者优先于写者,称为第一类读写问题.若读者在读数据时,有写者到来,则后续的读者将被阻塞,直到写者离开才被唤醒,这类问题中写者处于优势地位,叫第二类读写问题.

a)问题一:用记录型信号量解第一类读写问题

None.gif begin
None.gifvarRmutex,WRmutex:semaphore
= 1 , 1 ;
None.gifreadcount:integer
= 0 ;
None.gifcobegin
None.gif
None.gifprocessReader
None.gifbegin
None.gifrepeat
None.gifwait(Rmutex)
None.gifreadcount
= readcount + 1 ;
None.gif
if (readcount == 1 )wait(WRmutex); // 第一个读者开始读数据,禁止写者访问
None.gif
signal(Rmutex)
None.gif读数据
None.gifwait(Rmutex)
None.gifreadcount
= readcount - 1 ;
None.gif
if (readcount == 0 )signal(WRmutex); // 没有读者读数据唤醒写者访问
None.gif
signal(Rmutex)
None.gifuntil
false
None.gifend
None.gif
None.gifprocessWriter
None.gifbegin
None.gifrepeat
None.gifwait(WRmutex)
None.gif写数据
None.gifsignal(WRmutex)
None.gifuntil
false
None.gifend
None.gifcoend
None.gifend

注:
(1)WRmutex用于读者和写者,写者和写者之间互斥访问数据区.
(2)当没有读者访问时,允许一个读者进入,第一个读者进入时要用WRmutex与写者互斥.
(3) readcount记录正在读数据区的读者数量,由于能被多个读者进程共享,也是临界资源,因此用Rmutex来对其实施互斥访问.
(4)允许多个读者同时读数据,当至少有一个读者在读时,其他读者不用等待就可以读数据。
(5)一旦有一个读者开始读数据,其后,只要至少还有一个读者在读数据,读者就一直保持对数据区的控制权,这会造成写者的"饥饿";

b)问题二:用AND信号量解第一类读写问题

None.gif begin
None.gifvarL,mx:semaphore
= RN, 1 ; // 最多只能有RN个读者同时读数据
None.gif
cobegin
None.gif
None.gifprocessReader
None.gifbegin
None.gifrepeat
None.gifSwait(L,
1 , 1 ); // L》1表示读者数量不满RN个,此读者可以读
None.gif
Swait(mx, 1 , 0 ); // mx》1表示没有写者在写数据,此读者可读
None.gif
读数据
None.gifSsignal(L,
1 );
None.gifuntil
false
None.gifend
None.gif
None.gifprocessWriter
None.gifbegin
None.gifrepeat
None.gifSwait(mx,
1 , 1 ;L,RN, 0 ); // mx》1表示没有写者在写数据,同时L》RN表示没有读者在读数据
None.gif
写数据
None.gifSsignal(mx,
1 );
None.gifuntil
false
None.gifend
None.gifcoend
None.gifend
None.gif

c)第二类读写问题

None.gif begin
None.gifvarRmutex,WRmutex:semaphore,wx
= 1 , 1 , 1 ;
None.gifreadcount:integer
= 0 ;
None.gifcobegin
None.gif
None.gifprocessReader
None.gifbegin
None.gifrepeat
None.gifwait(wx)
None.gifwait(Rmutex)
None.gifreadcount
= readcount + 1 ;
None.gif
if (readcount == 1 )wait(WRmutex); // 第一个读者开始读数据,禁止写者访问
None.gif
signal(Rmutex)
None.gif读数据
None.gifwait(Rmutex)
None.gifsignal(wx)
None.gifreadcount
= readcount - 1 ;
None.gif
if (readcount == 0 )signal(WRmutex); // 没有读者读数据唤醒写者访问
None.gif
signal(Rmutex)
None.gifuntil
false
None.gifend
None.gif
None.gifprocessWriter
None.gifbegin
None.gifrepeat
None.gifwait(wx)
None.gifwait(WRmutex)
None.gif写数据
None.gifsignal(WRmutex)
None.gifsignal(wx)
None.gifuntil
false
None.gifend
None.gifcoend
None.gifend

3)哲学家进餐问题

a)使用记录型信号量

None.gif begin
ExpandedBlockStart.gifContractedBlock.gifvarchopstick[
5 ]:integer = dot.gif {1,1,1,1,1} ;
None.gifprocessPhilosopher
- i(i = 1 , 2 dot.gif 5 )
None.gifbegin
None.gifrepeat
None.gifwait(chopstick[i])
None.gifwait(chopstick[(i
+ 1 )mod5])
None.gif 吃饭
None.gifsignal(chopstick[i])
None.gifsignal(chopstick[(i
+ 1 )mod5])
None.gif 思考
None.gifuntil
false
None.gifend
None.gifend

b)使用AND信号量

None.gif begin
ExpandedBlockStart.gifContractedBlock.gifvarchopstick[
5 ]:integer = dot.gif {1,1,1,1,1} ;
None.gifprocessPhilosopher
- i(i = 1 , 25 )
None.gifbegin
None.gifrepeat
None.gif思考
None.gifSwait(chopstick[i],chopstick[(i
+ 1 )mod5])
None.gif吃饭
None.gifSsignal(chopstick[i],chopstick[(i
+ 1 )mod5])
None.gifuntil
false
None.gifend
None.gifend
None.gif

4.真题精选

[复旦2000]
问答: 在计算机科学文献中的几种互斥算法中,所谓的Lamport面包店算法可以有效地用于多个相互竞争的控制线程,该算法中线程之间的通信只能在共享内存中进行(即,不需要诸如信号量、原子性的set-and-test之类的专门机制)。该算法的基本思想源于面包店,因为面包店需要先取号然后等候叫号。下面给出了该算法的框架,该算法可以使各线程进出临界区而不产生冲突。

None.gif // declaration&initialvaluesofglobalvariables
ExpandedBlockStart.gifContractedBlock.gif
Enter,Number:array[ 1 ..N]ofinteger = dot.gif {0} ;
None.gif
None.gif
// logicusedbyeachthreaddot.gif
None.gif
// where"(a,b)<(c,d)"
None.gif
// means"(a<c)or((a==c)and(b<d))"
ExpandedBlockStart.gifContractedBlock.gif
Thread(i) dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif
while(true)dot.gif{
InBlock.gifEnter[i]
=1;
InBlock.gifNumber[i]
=1+max(Number[1],dot.gif,Number[N]);
InBlock.gifEnter[i]
=0;
ExpandedSubBlockStart.gifContractedSubBlock.gif
for(j=1;j<=N;++j)dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
while(Enter[j]!=0)dot.gif{
InBlock.gif
//waituntilthreadjreceivesitsnumber
ExpandedSubBlockEnd.gif
}

InBlock.gif
while((Number[j]!=0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
&&((Number[j],j)<(Number[i],i)))dot.gif{
InBlock.gif
//waituntilthreadswithsmallernumbers
InBlock.gif
//orwiththesamenumber,butwithhigher
InBlock.gif
//priority,finishtheirwork
ExpandedSubBlockEnd.gif
}

ExpandedSubBlockEnd.gif}

InBlock.gif
//criticalsectiondot.gif
InBlock.gif
Number[i]=0;
InBlock.gif
//non-criticalsectiondot.gif
ExpandedSubBlockEnd.gif
}

ExpandedBlockEnd.gif}

None.gif

定义(a,b)<(c,d)为(a<c) or (a=c and b<d);
Enter[i]和Number[i]可以被进程i读写,但是只能被进程j(j<>i)读.
请问:(1)算法如何解决互斥访问临界区问题
(2) 算法如何解决死锁问题
分析:每个进入面包店的顾客会得到一个号码,服务员为拥有最小号码的顾客服务.此算法需要进程pi经过两个步骤进入临界区.第一步,它需要选择一个号码,因此,它读取其他进程的号码,并选择一个比它们的最大值大1的数字作为自己的号码(称为"面包店入口).第二步,进程pi用如下方法检查是否可以进入临界区.对任意其他进程pj,pi首先检查pj是否已经在面包店入口,如果在,那么pi等待pj离开面包店入口,然后pi等待Number[j]变为0或者Number[j],j)<(Number[i],i).当pi对所有其他进程成功验证上述条件后,就可以进入临界区.

解:
首先证明两个结论:
(1)若进程pi已经在临界区,而且若干其他进程pk已经选择了他们的号码,那么(Number[i],i)<(Number[k],k).
证明:若pi已经在临界区,则一定经历了k轮for循环,就是说在上述几轮循环中,Number[k]=0或者(Number[i],i)<(Number[k],k).首先假设进程pi读出Number[k]为0,也就是说pk还没有选择好一个号码,这里有两种情形:一是pk不在面包店入口,二是已经进入但还没有退出.若pk不在面包店入口,则它将可以读到最新的Number[i]的值,从而确保Number[k]>Number[i].若它已经在入口处,则此次进入一定是在pi检查了Enter[k]之后,因为pi在检查条件(Number[k]==0)&&((Number[i],i)<(Number[k],k)))之前需要等待pk完成选择,也就是说pk将读到最新的Number[i]的值,因此确保Number[k]>Number[i].如果在k轮循环中,(Number[i],i)<(Number[k],k).那么这个结论将继续保持,因为Number[i]的值不会改变,而Number[k]只会增加.
(2)若进程pi在临界区中,则Number[i]>0
证明:因为此数值至少为0,当一个进程进入临界区之前一定执行了一个加1的操作.
由上述结论,若有两个进程pi和pk同时进入了临界区,由(2)知它们的号码都大于0,由(1),则有Number[k]>Number[i],反之也一样,所以矛盾.
对于死锁问题,由上述结论知,任一时刻一定存在一个进程pi,它的(Number[i],i)值最小,因此它总能够执行完成并归还资源,所以不会产生死锁.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值