实现进程互斥和同步的信号量机制
利用硬件技术实现进程同步机制
1.提高临界区代码执行中断优先级
这种方法在UNIX和Windows NT中都使用,它是在单机系统中有效地实现互斥的一种方法。
因为在传统操作系统中,打断进程对临界区代码的执行只有中断请求、中断一旦被接受,系统就有可能调用其它进程进入临界区,并修改此全局数据库。所以用提高临界区中断优先级方法就可以屏蔽了其它中断,保证了临界段的执行不被打断,从而实现了互斥。
在多处理机情况下,用提高临界段代码执行的中断优先级方法是无法保证互斥的,因为在一个处理机上提高中断优先级并不能阻止其它处理器上的中断,所以必须采用其它方法。
2.检测和设置TS硬件指令
许多大型机(如IBM370等)和微型机(如Intel ×86等)中都提供了专用的硬件指令,这些指令允许对一个字中的内容进行检测和修正,或交换两个字的内容。特别要指出的是这些操作都是在一个存储周期中完成,或者说由一条指令来完成,用这些指令就可以解决临界区问题了
在单机系统中,由于中断的原因,使得一个进程在对一个公用变量先取来并检测其值,然后修改的这两个动作中,可以插入其它进程对此公用变量的访问和修改,从而破坏了此公用变量数据的完整性和正确性。在多机系统中,多处理机共享主存,因而使得某处理机可插入另一处理机的两个存储访问周期之间,访问并修改此共享变量
对于同一主存块访问要求,即使两个处理机同时提出,存储控制逻辑也只能让其中之一先访问,但在一个处理机的两个存储周期间则可以插入另一个处理机的存储周期。现在用一条指令来完成检测和修改两个功能,这样中断和插入另一处理机的存储周期均不可能,所以不会影响此公用变量数据的完整性。
检测和设置(TS)的功能可用PASCAL语言描述如下:
这条指令在Z-8000中称为TEST指令,在IBM370中称为TS指令.
用这些硬件指令可以简单有效地实现互斥。其方法是为每个临界段或其它互斥资源设置一个布尔变量,例如称为lock。当其值为false则临界区末被使用,反之则说明正有进程在临界区中执行。于是某进程用TS指令实现互斥的程序结构为(设为无限循环进程):
WindowsNT 内核用来达到多处理器互斥的机制“转锁”,它类同于TS指令机制。
信号量机制(Semaphores)
记录型信号机制:
在信号量机制中信号量是代表资源物理实体的数据结构,记录型信号量的数据结构描述如下:
信号量的值只能通过两个原子操作:P、V操作来改变,它代表分配资源和释放资源
信号量类型
- 公用信号量(互斥信号量)
它为一组需互斥共享临界资源的并发进程而设置,代表共享的临界资源,每个进程均可对它施加P、V操作,即都可申请和释放该临界资源,其初始值置为1。信号量mutex取值意义如下:
mutex .value>=m ;m>0,表示m个某类资源空闲,可供使用。
mutex .value=0 ;表示资源已被占用,无其它进程等待。
mutex .value=-n ;n>0,表示资源已被占用,还有n个进程因 等待资源而阻塞。
- 专用信号量(同步信号量)
它为一组需同步协作完成任务的并发进程而设置,只有拥有该资源的进程才能对它施加P操作(即可申请资源),而由其合作进程对它施加V操作(即释放资源)。
P - V操作
对信号量施加P、V操作代表申请和释放资源,P、V操作描述如下:
P操作:
V操作:
利用信号量实现进程互斥
为使多个进程能互斥地访问某临界资源,只需为该资源设置一个互斥信号量mutex,并设其初值为1,然后将各进程的临界区CS置于P(mutex)和V(mutex)操作之间即可。利用信号量实现共享打印机的A、B两进程互斥的类并行PASCAL程序描述如下:
利用信号量实现进程同步
利用信号量能解决进程间的同步问题,这里以下图所示的计算进程C和打印进程P通过缓冲区Buffer传送数据的同步问题为例说明。
C和P两进程基本算法如下:
C和P两进程并发执行,必须在执行序列上遵循以下规则,才能避免错误:1.只有当C进程把数据送入Buffer后,P进程才能从Buffer中取出数据来打印,否则P进程只能等待。2.只有当P进程从Buffer中取走数据后,C进程才能将新计算的数据再存入Buffer,否则C进程也只能等待。
为了实现进程同步,需采用同步信号量。为了满足第一条同步规则,设置一个同步信号量full,它代表的资源是缓冲器满,它的初值为0。这个资源是P进程所拥有,P进程可以申请该资源,对它施加P操作,如条件满足P进程可从Buffer中取数。而P进程的合作进程C对full信号量施加V操作,即它可释放该资源。当C进程将数据存入Buffer后,即可释放该资源供P进程再使用。同样为了满足第二条同步规则,设置另一个同步信号量empty,它代表的资源是缓冲器空,它的初值为1 。缓冲器空这个资源是进程C所拥有,它可以申请该资源,对它施加P操作。而它的合作进程P对empty信号量施加V操作。
实现C和P两进程同步的类PASCAL程序:
利用信号量描述前趋关系
进程间同步关系也可用前趋图表示。C和P两进程间先计算好再打印同步关系用前趋图表示如下:
对应这个前趋关系可设置同步信号量full,它为后继进程P拥有,初值为0。它的并发执行程序如下:
利用信号量解决经典进程同步进程
经典进程同步问题是从进程并发执行中归纳的典型例子,这些问题常用来测试新的同步机制可行性。主要的经典同步问题有生产者-消费者问题、读者-写者问题、哲学家进餐问题等。
生产者-消费者问题
生产者-消费者问题是最著名的同步问题,它描述一组生产者(P1 ……Pm)向一组消费者(C1……Cq)提供消息。它们共享一个有界缓冲池(bounded buffer pool),生产者向其中投放消息,消费者从中取得消息,如下图所示。生产者-消费者问题是许多相互合作进程的一种抽象。
假定缓冲池中有n个缓冲区,每个缓冲区存放一个消息。由于缓冲池是临界资源,它只允许一个生产者投入消息,或者一个消费者从中取出消息。即生产者之间、生产者与消费者之间、消费者之间都必须互斥使用缓冲池。所以必须设置互斥信号量mutex,它代表缓冲池资源,它的数值为1。
与计算打印两进程同步关系相同,生产者和消费者二类进程P和C之间应满足下列二个同步条件:
只有在缓冲池中至少有一个缓冲区已存入消息后,消费者才能从中提取消息,否则消费者必须等待。
只有缓冲池中至少有一个缓冲区是空时,生产者才能把消息放入缓冲区,否则生产者必须等待。
为了满足第一个同步条件,设置一个同步信号量full,它代表的资源是缓冲区满,它的初始值为0,它的值为n时整个缓冲池满。这个资源是消费者类进程C所拥有,C进程可以申请该资源,对它施加P操作,而C进程的合作进程生产者进程P对它施加V操作。同样为了满足第二个同步条件,设置另一个同步信号量empty,它代表的资源是缓冲区空,它的初始值为n,表示缓冲池中所有缓冲区空。
用类并行PASCAL语言和信号量机制解生产者-消费者问题程序:
var mutex,empty,full:semaphore:=1,n,o ;
Buffer : array [0……n-1] of message ;
in, out : o……n-1:=0,0 ;
begin
parbegin
P: begin
repeat
Produce a new message m ;
P (empty) ;
P (mutex) ;
Buffer[in]=m ;
in :=(in+1) mod n ;
V (mutex) ;
V (full) ;
until false
end
C: begin
repeat
P (full) ;
P (mutex) ;
m := buffer[out] ;
Out : = (out+1) mod n ;
V (mutex) ;
V (empty) ;
Consume message m ;
until false
end
parend
end
读者-写者问题
一个数据集(如文件)如果被几个并行进程所共享,有些进程只要求读数据集内容,它称读者,而另一些进程则要求修改数据集内容,它称写者,几个读者可以同时读些数据集,而不需要互斥,但一个写者不能和其它进程(不管是写者或读者)同时访问些数据集,它们之间必须互斥。
设置互斥信号量wmutex 表示写者间、读者和写者间互斥,读者和写者主要程序如下:
reader: writer:
第一个读者到时P(wmutex) P(wmutex)
Read Text Write Text
最后一个读者离开时V(wmutex) V(wmutex)
用readcount变量来记录读者数,读者程序为:
if readcount=0 then P(wmutex);
readcount= readcount+1;
Read Text
readcount= readcount-1;
if readcount=0 then V(wmutex);
由于readcount是读者间共享变量,属于临界资源,它也需互斥,为此又增设互斥信号量rmutex.
用类并行PASCAL语言和信号量机制解读者-写者问题程序:
Var rmutex,wmutex: semaphore:=1,1 ;
readcount :integer :=0 ;
begin
parbegin
reader:begin
repeat
P(rmutex)
if readcount=0 then P(wmutex);
readcunt=readcount+1;
V(rmutex)
Read Text
P(rmutex)
readcount=readcount-1 ;
if readcount=0 then V(wmutex) ;
V(rmutex)
until false
end
parend
writer:begin
repeat
P(wmutex);
Write Text;
V(wmutex);
until false
end
AND型/一般信号集机制
为了解决一个进程一次同时能申请几类临界资源,引入AND型信号量集机制(申请一个);为了对每类资源又能同时申请几个,又扩充AND型信号量集机制,形成一般信号量集机制,它的相应的Swait、 Ssignal操作如下(t 代代表系统可用最低量, d i 代表申请量):
Swait(S1 , t 1 ,d 1 , …, Sn , t n , d n )
if S1 >= t 1 and …..and Sn >=t n then
for i:=1 to n do Si = Si - d i ;
endfor
endif
else
Place the executing process in the waiting queue of the first Si with Si< t i and set its program counter to the beginning of the the Swait operation.
Ssignal(S1 ,d 1 , …, Sn , d n )
for i:=1 to n do Si = Si + d i ;
Remove all the process waiting in the queue associated with Si into the ready queue.
endfor
用一般信号集机制解读者写者问题
L表示阅读权利,最大为RN,mx表示写权利,数量为1
var RN integer ;
L , mx : semaphore :=RN ,1 ;
begin
parbegin
reader : begin
repeat
Swait (L , 1 , 1 ) ;
Swait (mx , 1 , 0 ) ;
perform read operation
Ssignal (L , 1) ;
until false ;
end
writer: begin
repeat
Swait (mx ,1, 1, L , RN , 0 );
perform write operation
Ssignal (mx , 1 ) ;
until false ;
end
parend
end
管程
-
管程的引入
用信号量和P、V操作来解决进程互斥同步问题,需在程序中适当位置安排P、V操作,否则会造成死锁错误,为了解决分散编程带来的困难,采用资源集中管理的方法,用某种数据结构表示某类资源,并将这种数据结构以及与资源有关操作集中一起定义为类程或管程,并用类程管理独立资源,用管程管理共享资源,它两是能被进程调用的实体。 -
管程的定义
管程是由一些共享数据、能为并发进程所执行的作用在共享数据上的操作的集合、初始代码以及存取权组成。
管程提供了一种可以允许多进程安全有效地共享抽象数据类型的机制,管程实现同步机制由“条件结构”(condition construct)所提供。为实现进程互斥同步,必须定义一些条件变量,(例如:var notempty,,notfull:condition).这些条件变是只能被wait和signal 操作所访问。
notfull . wait操作意味着调用该操作的进程将被挂起,直至另一个进程执行。
notfull . signal操作仅仅是启动一个被挂起的进程,如无挂起进程则notfall . signal操作相当于空操作,不改变notfull状态,这不同于v操作。
-
管程的特点
- 管程中的临界资源用数据结构抽象表示,只有定义在数据结构上的管程所提供的过程才能访问该数据结构。因此,管程保护了管程中的信息。
- 共享临界资源的进程通过调用管程提供的过程来使用临界资源。但是,在任一时刻,只有一个进程才能调用管程提供的过程,即并发进程互斥调用管程提供的过程,不能调用管程中过程的进程只能等待。
- 任一时刻,在管程中只能有一个进程运行。调用管程中过程的进程进入管程,如果不能访问临界资源时,则不能继续运行,需要在管程中进入阻塞状态,等待临界资源。此时,在管程外等待访问管程的另一个进程可以进入管程。同理,当进程访问管程结束,需要调用管程提供的退出过程。
- 在管程中等待临界资源的进程只能由访问同一资源的进程唤醒,即访问完临界资源的进程需要唤醒阻塞等待同一资源的进程。
-
管程的结构
利用管程解决生产者-消费者问题
建立一个管程PC,它包括两个过程put(item)和get(item),它们分别执行将生产的消息放入缓冲池和从缓冲池取出消息的操作,设置一变量count表示缓冲池已存消息数目。
Type PC=monitor
var in , out , count : integer ;
buffer : array [ 0 , … ,n-1] of item ;
notfull ,notempty :condition ;
procedure entry put (item)
begin
if count >= n then notfull.wait ;
beffer ( in ) : = nextp ;
in := (in+1) mod n ;
count = count + 1 ;
if notempty.queue then notempty.signal ;
end
procedure entry get ( item)
begin
if count <= 0 then notempty.wait ;
nextc := buffer ( out ) ;
out := (out+1) mod n ;
count := count - 1 ;
if notfull.queue then notfull.signal ;
end
begin in := out := 0 ; count := 0; end
生产者和消费者程序为:
producer : begin
repeat
produce an item in nextp ;
PC.put ( item) ;
until false ;
end
consumer : begin
repeat
PC.get (item) ;
consume the item in nextc ;
until false ;
end