390 likes | 527 Views
第五章 并行性 : 同步与互斥. 概论 临界段 互斥 信号量 *管程 * UNIX 中的同步和互斥机制. 5.1 概论 : 进程间的关系. 多道程序系统的特征 : 并行性,制约性,动态性 进程间的 相互制约关系 : 同步关系 (直接制约) : 多个进程共同完成一个任务 , 它们之间必须协同动作 , 互相配合 , 相互交换信息 -- 进程通信 互斥关系 (间接制约) : 多个进程共享资源,互斥资源的使用具有排它性,因此进程间往往需要互相竞争,以使用这些互斥的资源。也可看成一种特殊的同步关系 ! 重点讨论互斥问题.
E N D
第五章 并行性:同步与互斥 • 概论 • 临界段 • 互斥 • 信号量 • *管程 • *UNIX中的同步和互斥机制
5.1 概论:进程间的关系 • 多道程序系统的特征: • 并行性,制约性,动态性 • 进程间的相互制约关系: • 同步关系 (直接制约): 多个进程共同完成一个任务,它们之间必须协同动作, 互相配合,相互交换信息--进程通信 • 互斥关系 (间接制约): 多个进程共享资源,互斥资源的使用具有排它性,因此进程间往往需要互相竞争,以使用这些互斥的资源。也可看成一种特殊的同步关系! • 重点讨论互斥问题
5.2 临界段(Critical Section)问题 • 当两个以上进程共享某个变量时,一次只能允许一个进程对共享变量进行写操作(即互斥地对共享变量进行访问). • 如进程 Pi:...;x:=x+1;...(i=1,2)处理机C1上执行进程P1,有内部通用寄存器R1; 处理机C2上执行进程P2,有内部通用寄存器R2. • 该赋值语句由三个机器内部动作组成: R:=x; R:=R+1; x:=R; • P1:…;R1:=x; R1:=R1+1; x:=R1;… P2:…; R2:=x; R2:=R2+1; x:=R2;… • P1:…;R1:=x; R1:=R1+1; x:=R1;… P2:…; R2:=x;R2:=R2+1;x:=R2;… t
临界段设计原则 • 临界段:进程中访问共享变量的代码段。 • 临界资源:允许多个进程共享使用的资源。 • 访问同一临界资源的各临界段分散在各有关进程的程序中。 • 临界段设计原则: • 每次至多只允许一个进程处于临界段之中; • 若有多个进程同时要求进入它们的临界段时,应在有限时间内让其中之一进入临界段,而不应相互阻塞,以至于各进程都无法进入临界段; • 进程只应在临界段内逗留有限时间。 • 实现方法: • 软件方法(复杂,局限性大); • 硬件指令(简单实用,使用广泛); • 信号量(P,V操作)
5.3.1*实现互斥的软件方法 • 两个并行进程P0,P1互斥共享单个资源. • i, j : 取值为0,1. 即i=0时, j=1; i=1时j=0. • 五种尝试
软件解决方法一:进程标志位 • varflag:array[0..1] of boolean; Pi:begin repeat while flag[j]do skip; flag[i]:=true;进程Pi的临界段代码CSi; flag[i]:=false; 进程Pi的其它代码; forever end • flag[i]= true 进程i在临界段中执行; flag[i]= false 进程i不在临界段中执行。 • 问题:当两个进程的标志位初值均为false时,则都进入—违背原则一!
软件解决方法二:临界段进程指针 • var turn:0..1; Pi:begin repeat while turn<>i do skip; 进程Pi的临界段代码CSi; turn:=j; 进程Pi的其它代码; forever end • 若turn=i, 则进程Pi进入临界段 • 问题:强制进程以交替顺序进入临界段,即使发生等待也不可以打破!(临界资源利用率不高)
软件解决方法三:改进解法一 • Pi:begin repeatflag[i]:=true; while flag[j] do skip; 进程Pi的临界段代码CSi; flag[i]:=false; 进程Pi的其它代码; forever end • 用标志位flag[i]表示进程i想进入临界段 • 问题:两个进程同时想进入临界段时,各自标志位置true;检测对方状态时发现对方也想进入,于是互相等待而阻塞了自己
软件解决方法四:Dekker算法 • var flag:array[0..1] of boolean; /*初值false turn:0..1; Pi:beginrepeat flag[i]:=true;whileflag[j] do if turn=jthenbegin flag[i]:=false; whileturn=j do skip; flag[i]:=true; end进程Pi的临界段代码CSi; turn:= j; flag[i]:=false;进程Pi的其它代码;forever end • flag[i]=true 进程Pi要进入临界段 • turn=i 允许进程Pi进入临界段
软件解决方法五 • var flag:array[0..n-1] of (idle,want_in,in_cs); turn:0..n-1; Pi:begin var j:integer; repeat repeat flag[i]:=want_in; while turn<>i do if flag[turn]=idle then turn:=i; flag[i]:=in_cs; j:=0; while (j<n)and (j=i or flag[j]<>in_cs) do j:=j+1; until j>=n; 。。。。。。 • Dekker算法扩充到n个进程共享一个公用变量的情况.
5.3.2实现互斥的硬件技术 • 硬件指令 • 信号量:P,V操作--对信号量的控制
硬件指令 • 特点: 通常对公用变量先读取检测,再修改要由2~3条指令完成. • 允许对一个字中的内容进行检测和修正, 或交换两个字的内容. • 操作在一个存储周期中完成, 即由一条指令完成. • 好处: • 用一条指令中即可实现检测和修改 • 单机系统: 避免了因中断而在检测和修改之间插入其他进程对此公用变量的修改; • 多机系统: 避免插入另一处理机的存储周期访问并修改此变量,从而影响公用变量的完整性和正确性. • 单机系统: 提高进程临界段的中断优先级,不让中断来打扰. • 指令: TS(Test-and-Set), Swap.
检测和设置指令(功能描述) • function Test_and_Set(var flag:boolean):boolean begin Test_and_Set:=flag; flag:= true; end • Z-8000: TEST ; IBM370: TS
交换指令(功能描述) • 交换两个字的内容. • procedureSwap(vara,b:boolean) var temp:boolean; begin temp:=a; a:=b; b:=temp; end • Intel 8086/8088: XCHG
为每个临界段或其它互斥资源设置一个布尔变量, 如lock: false_ 临界段未被用 true _ 正有进程在临界段 中运行 repeat while TS(lock) do Skip; 进程Pi的临界段代码CS; lock:=false;进程Pi的其它代码;forever repeat key:=true; repeat Swap(lock,key); until key=false;进程Pi的临界段代码CS; lock:=false;进程Pi的其它代码; forever 问题:“忙等待”! 用硬件指令实现进程互斥: 加锁/解锁
begin … end repeat ... Forever repeat ... until P while P do begin ...end function name1(...):type procedure name2(...) var i,j:integer; var x:array[0..9] of integer; i:=1; <>,=,and,or,not {…} for (;;)或while 1 do {...} do{...}while !P while P do{...} type name1(...) void name2(...) int i,j; int x[10]; i=1; !=,==,&&,||,! 类PASCAL描述语句与C对比
5.4信号量semaphore • 信号量:一个仅能用同步原语对其进行操作的整形变量. • 二元信号量:仅允许取值为0与1,主要用来管理互斥变量(互斥信号量). • 一般信号量:允许取值为非负整数,主要用于进程间的一般同步问题(应用中可扩充为负)。 • 注意:信号量不同于普通变量!只能用Wait,Signal操作修改! • 同步原语:不可中断 • Wait(S):当信号量S>0时将S减1。否则进程等待,直到其它进程对S进行V操作。 • Signal(S):将信号量S加1。
Wait,Signal操作的实现 • 依进程等待方式不同,Wait操作的执行方式也不同。 • 忙等待:当进程必须等待时,如果预计等待时间不长,则在多机系统中,为了减少进程状态转换所引起的开销,可采用忙等待方式。 • 阻塞等待:当进程必须在信号量S上等待时,将进程变为等待状态(blockeda),插入与信号量S有关的等待队列,让出CPU。(普遍采用,尤其在单机系统!)信号量定义: type Semaphore = record value:integer ; L : pointer to PCB; end struct Semaphore {int value; PCB *L; }
Wait,Signal操作的“忙等待”执行方式 • Wait(S): While S<=0 do skip;S:=S-1; • Signal(S): S:=S+1;
Wait,Signal操作的“阻塞等待”执行方式 • Wait(S):S.value:=S.value-1; if S.value<0 then begin Insert(CALLER,S.L); Block(CALLER); end • Signal(S):S.value:=S.value+1; if S.value<=0 then begin Remove(S.L,id); Wakeup(id); end
信号量及Wait,Signal操作的物理意义 • S>0时,信号量值表示该类资源的可用资源数 • S<=0时,表示已无此类资源可供分配,请求资源的进程将被阻塞在相应的信号量S的等待队列中。 S的绝对值 = 该信号量上等待的进程数。 • S>0时,每执行一次Wait操作,意味着请求分配一个单位的该类资源给执行P操作的进程,即 S:=S-1; • S<=0时,每执行一次Signal操作,意味着进程释放一个单位的该类可用资源,即S:=S+1.而此时若S等待队列中有因该资源而被封锁的进程(blockeda),则把队列中的一个进程唤醒,转入就绪队列(readya)。
5.4.4同步机构应用 • 用信号量实现进程间的互斥; • 用信号量实现进程的阻塞和唤醒; • 用信号量实现进程间同步: 经典同步问题: • 生产者和消费者问题; • 阅读者和写入者问题; • 五位哲学家进餐的问题(管程方法);
用信号量实现进程间的互斥 • 资源:临界段;可用单位数:1 • mutex:互斥信号量(mutual exclusion),最多只允许一个进程进入临界段,相当于一把锁! • 初值=1,在n个进程间实现互斥,则只有一个进程能用P操作把mutex减为0,而执行其临界段! • 第二个进程对mutex执行P操作时,mutex=-1, 进程被阻塞。如此继续... • 某进程对mutex执行V操作,mutex加1,当mutex<=0时唤醒等在mutex上的一个进程使之进入就绪状态继而进入临界段! • 信号量mutex的取值范围:1..-(n-1) • 最多n-1个进程等待进入临界段
互斥信号量mutex在n个进程间实现互斥 • var mutex:Shared Semaphore; begin mutex:=1; cobegin P1:…; Pi: repeat P(mutex); 进程Pi的临界段代码CS; V(mutex); 进程Pi的其它代码 forever Pn:…; coend end
信号量实现进程的阻塞和唤醒 • 资源的分配和释放:进程所等待的资源被其他进程释放成为可用(互斥)。 • 信号量:每类资源一个,初值=资源的数量(资源记数器)。 • P操作:资源分配机构。信号量非正,进程被阻塞。 • V操作:资源释放机构。信号量非正,重新调度以唤醒一阻塞进程。 • 进程间相互协同工作:等待相关的协作进程完成某个操作(同步)。 • 信号量:每个进程一个(同步进程),初值为0。 • P操作:等待时阻塞自己。P(Proc-sem(进程内部标识号id)) • V操作:被所等待的进程唤醒。V(Proc-sem(i0))
协同进程举例 • 进程i要等待j的计算结果 • 进程 i 进程 j 进行计算;进行计算; P(Proc_sem(i)); V(Proc_sem(i)); 继续计算; 继续计算; • 阻塞:自己阻塞自己,不能阻塞其他进程(进程自己在非正信号量上执行P操作); • 唤醒:由其他进程唤醒,不能自己唤醒自己(其他进程在信号量为-1时执行V操作)。
生产者和消费者问题 • 计算机系统中的常见问题: 输入设备 Buffer Buffer 打印设备 输入进程Pi 计算进程Pc 打印进程Pp • 输入时:Pi是生产者,Pc是消费者; • 输出时:Pc是生产者,Pp是消费者 • 一组生产者向一组消费者提供产品——数据,共享一个缓冲池——临界资源。生产者向其中投放数据,消费者从中取出数据消费。生产者和消费者问题——>OS中进程合作的一种抽象。
问题的处理 • 缓冲池:N个缓冲区 • P:一组生产者共用的指向 空缓冲区头的指针; • C:一组消费者共用的指向 满缓冲区头的指针。 • 互斥操作: • 分配空缓冲区和移动指针P; • 释放满缓冲区和移动指针C; • 信号量: • E-空缓冲区资源信号量; F-满缓冲区资源信号量。 • Me-指针P操作互斥信号量; Mf-指针C操作互斥信号量。 满 C P 空
var E,F,Me,Mf:Shared Semaphore begin E:=N; F:=0; Me:=Mf:=1; cobegin Producers begin repeat P(E); P(Me); “分给空缓冲区并调整指针P的临界段”;V(Me); “向缓冲区中装入数据”;V(F); forever end 练习:4-20 Consumers begin repeat P(F); P(Mf); “分给满缓冲区并调整指针C的临界段”;V(Mf); “从缓冲区中取出数据”;V(E); forever end coendend 生产者/消费者问题————进程同步关系的描述
var mutex,empty,full:Shared Semaphore; • buffer:array[0..n-1] of message; • mutex:=1;empty:=n;full:=0; • in,out:0..n-1; • begin • cobegin • producer:begin repeat... Produce message m; P(empty); P(mutex); buffer(in):=m;in:=(in+1) mod n; V(mutex);V(full); forever end • consumer:begin repeat … P(full); P(mutex); m:=buffer(out);out:=(out+1) mod n; V(mutex);V(empty); consume message m; forever end • coend • end
阅读者和写入者问题 • N个并行进程共享一个数据集,有些进程只要求读(阅读者),而另一些进程要求修改(写入者)——阅读者/写入者问题! • 原则: • N个阅读者:无互斥问题! • 一个写入者:与其他进程互斥! • 当无写入者正在访问共享数据集时,阅读者可以进行访问,否则必须等待。 • Readcount:记录正在读的阅读者人数。 • Readcount的访问:对阅读者是互斥的--用mutex作访问的互斥信号量! • 共享数据集的访问:对写操作互斥的--用wrt作访问的互斥信号量!
var mutex,wrt:Semaphore; readcount:integer; mutex:=wrt:=1; readcount:=0; cobegin Readeri: begin P(mutex); readcount:=readcount+1; if readcount=1 then P(wrt); V(mutex); 读数据集; P(mutex); readcount:=readcount-1; if readcount=0 then V(wrt); V(mutex); end Writeri: begin P(wrt); 写数据集; V(wrt); end; coend 练习: 读者优先=> 写入者优先
Var m,w,s,mutwx,k:semaphore; RC,WC:integer; begin m:=w:=s:=mutex:=k:=1; RC:=WC:=0; cobegin READER:repeat P(m);P(w);P(mutex); if RC=0 then P(s); RC:=RC+1; V(mutex);V(w);V(m); 阅读; P(mutex); RC:=RC-1; if RC=0 then V(s); V(mutex); forever WRITER:repeat P(k); if WC=0 then P(w); WC:=WC+1; V(k); P(s); 写入; V(s); P(k); WC:=WC-1; if WC=0 then V(w); V(k); forever coend end 写入优先
5.5 管程的概念 • 互斥手段(测试和设置命令,P,V操作)的不足: • 由用户编写控制代码:加重了用户负担! • P,V操作分散在用户程序中,系统无法有效地控制和管理; • 用户编程时若使用P,V操作不当,后果严重(死锁),编译程序或OS无法发现和纠正。 • 解决方法:为每个共享资源设立一个秘书(管程monitor),每次只允许一个进程访问共享资源. • 管程:是管理进程间同步的机制,它保证进程互斥地访问共享变量,并且提供了一个方便的阻塞和唤醒进程的机构。 • 管 局部于该管程的数据结构——共享变量 程 局部于该管程的对于上述数据结构进行规定操作的若干个过程
5.6 进程间的通信 • 作业->若干个可并行执行的进程->协同完成一个工作(同步)->进程通信(在进程之间交换一定数量的信息)。 • 进程通信方式: • 低级通信原语:交换信息量较少。如互斥,同步机构。 • 高级通信原语:交换信息量较多。如直接通信,间接通信。 • 高级通信原语: • 直接通信:一个进程直接发送消息给接受者进程; Send( P, Msg ); Receive( P, Msg ); • 间接通信:进程通过一个“信箱”来传递消息。 Send( A, Msg ); Receive( A, Msg );
五位就餐的哲学家问题 0 1 • 问题:5位哲学家,一张圆桌,5把叉子,吃时需左右两把! • 哲学家状态: • 思考问题; • 饥饿吃通心粉。 • 要求: • 相邻两位不能同时进餐; • 最多只能有两人同时进餐,即最大并行性为2; • 进餐时先拿右边的叉子,后拿左边的叉子; • 餐后先放右边的叉子,后放左边的叉子; • 用管程来实现用叉过程的管理 4 2 3
数据描述 • 管程的构成: • 描述共享资源--叉子状态的数据结构: Forks(i)指第i个哲学家可用叉子数,0,1,2; • 描述对叉子的操作过程ForksControl: Pickup(i),Putdown(i):管程的入口.每次只允许一个进 程进入其中之一. • Left(i),Right(i):分别为第i个哲学家的左右邻哲学 家号; • Ready(i):表示第i个哲学家条件变量的集合. False:表示叉子数<2,未准备好! • 过程Wait,signal由并行PASCAL提供,相当于P,V操作; • Hung表示哲学家饥饿否;(hungry)
管程问题描述框架 0 1 4 • ForksControl:monitor type T=array[0..4]of integer; B=array[0..4]of boolean;var Forks,Right,Left:T; Ready:B; i:integer; • procedure entry PickUp(var me:integer); begin if Forks(me)<>2 thenwait(Ready(me)); Forks(Right(me)):=Forks(Right(me))-1; Forks(Left(me)):=Forks(Left(me))-1; end • procedure entry PutDown(var me:integer); begin Forks(Right(me)):=Forks(Right(me))+1; Forks(Left(me)):=Forks(Left(me))+1; if Forks(Right(me))=2 thenSignal(Right(me)); if Forks(Left(me))=2 then Signal(Left(me)); end 2 3
过程描述 赋初值 • begin for i=0 to 4 do begin Forks(i):=2; Right(i):=[i+1] mod 5; Left(i):=[i+4] mod 5; Ready(i):= false; end cobegin Philosopher 0:Process(var hungry:boolean); begin repeat if hungry=true then begin ForksControl.PickUp(0); 吃通心粉; ForksControl.PutDown(0); end思考问题; forever end Philosopher 1:Process;… ... Philosopher 4:Process;…coend end 0 1 4 2 3