1 操 作 系 统 | 进 程 的 同 步 与 通 信 1 CUIT 徐虹 第三章进程的同步与通信 ?并发执行的进程之间的关系 ?进程的互斥与同步问题 ?进程间的高级通信 操 作 系 统 | 进 程 的 同 步 与 通 信 2 CUIT 徐虹 3.1进程互斥 ?并发引起的操作系统的改变 ?操作系统必须记住各种活跃的进程 ?必须为每个进程分配和释放各种资源 ?保护每个进程的数据和物理资源 ?进程的结果必须与执行速度无关 操 作 系 统 | 进 程 的 同 步 与 通 信 3 CUIT 徐虹 ?进程间的制约 系统中的诸进程可以共行执行,并以 各自独立的速度向前推进。但由于资源共 享及进程合作(即诸进程协同完成一项共 同的任务),因而产生了进程之间的相互 制约。 ?临界区 ?临界资源(Critical Resource):一次 仅允许一个进程使用的资源。 操 作 系 统 | 进 程 的 同 步 与 通 信 4 CUIT 徐虹 例1:两个进程对同一变量count访问和修改, P1:count+=1; P2:count-=1; 若count = 5。 P1:R1 = count; R1 = R1+1; count = R1; P2:R2 = count; R2 = R2 – 1 ; count = R2; 若顺序执行P1、P2,则count = 5。 操 作 系 统 | 进 程 的 同 步 与 通 信 5 CUIT 徐虹 P1:R1 = count; P1:R1 = count; P2:R2 = count; R1 = R1+1; P1:R1 = R1+1; P2:R2 = count; count = R1; R2 = R2 – 1; P2:R2 = R2 – 1; count = R2; count = R2; P1:count = R1; 若执行顺序如上,若执行顺序如上, 则count = 4则count = 6。 操 作 系 统 | 进 程 的 同 步 与 通 信 6 CUIT 徐虹 例2. 进程P1、P2共同调用函数echo()。 void echo() { chin = getchar(); chout = chin; putchar(chout); } 2 操 作 系 统 | 进 程 的 同 步 与 通 信 7 CUIT 徐虹 Process P1 Process P2 .. chin = getchar(); . . chin = getchar(); chout = chin; . chout = chin; putchar(chout); . . putchar(chout); .. 操 作 系 统 | 进 程 的 同 步 与 通 信 8 CUIT 徐虹 ?临界区:不允许多个并发进程交叉执行的一段程序。 cobegin process 1:begin L1:CS1; Remainder of process 1; Goto L1; end process 2:begin L2:CS2; Remainder of process 2; Goto L2; end coend 操 作 系 统 | 进 程 的 同 步 与 通 信 9 CUIT 徐虹 ?互斥 ?不允许两个以上的共享该资源的并 发进程同时进入临界区称为互斥。 ?临界区调度原则 ?空闲让进 ?忙则等待 ?有限等待 ?让权等待 操 作 系 统 | 进 程 的 同 步 与 通 信 10 CUIT 徐虹 ?互斥的软件实现 进程Pi,Pj共享临界资源R。Turn:进程编号。 ?算法1:共享一个公用整型变量turn Pi :repeat while turn <> i do no_op; critical section turn := j; remainder section until false; 操 作 系 统 | 进 程 的 同 步 与 通 信 11 CUIT 徐虹 ?算法2:用数组代替算法一中的turn var flag : array [0… n] of boolean = flase; repeat while flag[j] do no_op; flag[i] := true ; critical section flag[i] := false ; remainder section until false ; 操 作 系 统 | 进 程 的 同 步 与 通 信 12 CUIT 徐虹 ?算法3: repeat flag[i] : = true ; while flag[j] do no_op; critical section flag[i] := false ; remainder section until false ; 3 操 作 系 统 | 进 程 的 同 步 与 通 信 13 CUIT 徐虹 ?算法4:进程共享两个公用变量 Pi: repeat flag[i] : = true ; turn := j ; while ( flag[j] and turn = j ) do no_op; critical section flag[i] := false ; remainder section until false ; 操 作 系 统 | 进 程 的 同 步 与 通 信 14 CUIT 徐虹 ?算法5:对临界区S设置一个变量状态W, W=0表示资源可用,W=1表示资源已被占用。 例:Begin integer W = 0 ; Cogegin Begin L1:lock W; CS1; Unlock W; Remainder of process 1; Goto L1; End Begin L2:lock W; CS2; Unlock W; Remainder of process 2; Goto L2; End Coend End 操 作 系 统 | 进 程 的 同 步 与 通 信 15 CUIT 徐虹 ?关锁原语 lock W:if W = 1 then begin block(n); insert(WL,n); scheduler; end else W = 1; 操 作 系 统 | 进 程 的 同 步 与 通 信 16 CUIT 徐虹 ?开锁原语 unlock W:if WL<> NULL then begin remove(WL,q); status(q) = “ready”; insert(RL,q); end else W = 0; 操 作 系 统 | 进 程 的 同 步 与 通 信 17 CUIT 徐虹 ?硬件实现进程互斥 ?利用Test_and_Set 指令 function TS (var lock : boolean ) : boolean ; begin TS := lock ; Lock := true ; End Lock = true :资源被占用 Lock = false :资源可用 操 作 系 统 | 进 程 的 同 步 与 通 信 18 CUIT 徐虹 利用TS指令实现互斥: repeat while TS (lock) do skip ; critical section lock := false ; remainder section until false ; 4 操 作 系 统 | 进 程 的 同 步 与 通 信 19 CUIT 徐虹 ?利用Swap指令实现进程互斥 初值lock = false 表示资源可用。 Procedure Swap (var a,b : boolean) Var temp : boolean ; Begin Temp := a ; a := b ; b := temp ; End 操 作 系 统 | 进 程 的 同 步 与 通 信 20 CUIT 徐虹 repeat key := true ; repeat Swap(lock,key) ; Until key = false ; critical section lock := false ; remainder section until false ; 操 作 系 统 | 进 程 的 同 步 与 通 信 21 CUIT 徐虹 3.2信号量机制 ?整型信号量机制 ?信号量(sem) sem>=0: 表示可供并发进程使用的资源实体的个数 sem<0: 表示正在等待使用临界区的进程数。 Sem的值只能由P、V操作改变。 P:wait ( s ) V:signal (s ) 按信号量数值分为二元信号量和一般信号量。 操 作 系 统 | 进 程 的 同 步 与 通 信 22 CUIT 徐虹 ?Wait(P)和signal(V)原语 ?Wait(s) 原语 wait(s) :Begin Lock out interrupts; s = s – 1; If s < 0 then Begin Status(q) := blocked; Insert(WL, q); Unlock interrupts; Scheduler; End Else unlock interrupts; End 操 作 系 统 | 进 程 的 同 步 与 通 信 23 CUIT 徐虹 ?signal原语 signal(s) :Begin Lock out interrupts; s = s + 1; If s < =0 then Begin Remove(WL,R); Status(R) := ready; Insert(RL,R); End Unlock interrupts; End 操 作 系 统 | 进 程 的 同 步 与 通 信 24 CUIT 徐虹 ?利用信号量实现互斥 Begin integer mutex = 1; cobegin process1:Begin L1:wait (mutex);CS1; signal (mutex); Remainder of process 1; Goto L1; End Process2:Begin L2:wait (mutex);CS2; signal (mutex); Remainder of process 2; Goto L2; End Coend End 5 操 作 系 统 | 进 程 的 同 步 与 通 信 25 CUIT 徐虹 例、进程A、B、C依赖于D的结果 操 作 系 统 | 进 程 的 同 步 与 通 信 26 CUIT 徐虹 操 作 系 统 | 进 程 的 同 步 与 通 信 27 CUIT 徐虹 ?利用信号量描述前趋关系 P1:S1 P2:S2 S1——>S2 则设置信号量s=0, P1:S1;signal(s); P2:Wait(s);S2; 操 作 系 统 | 进 程 的 同 步 与 通 信 28 CUIT 徐虹 1 2 3 4 5 6 7 a b c d e f g h 例:根据前趋图写出可并发执行的程序: var a,b,c,d,e,f,g,h:semaphore := 0,0,0,0,0,0,0,0; 操 作 系 统 | 进 程 的 同 步 与 通 信 29 CUIT 徐虹 begin parbegin begin S1;signal(a); signal(b); signal(c); end begin wait(a); S2; signal(d); end begin wait(b); S3; signal(e); end begin wait(c); S4; signal(f); end begin wait(d); S5; signal(g); end begin wait(e); wait(f); S6 ; signal( h); end begin wait(g); wait(h); S7; end parend end 操 作 系 统 | 进 程 的 同 步 与 通 信 30 CUIT 徐虹 ?记录型信号量机制 type semaphore = record value:integer; L:list of process; end 6 操 作 系 统 | 进 程 的 同 步 与 通 信 31 CUIT 徐虹 procedure wait(s) var s:semaphore; begin S.value := S.value – 1; If S.value < 0 then block(S,L); end procedure signal(s) var s:semaphore; begin S.value := S.value + 1; If S.value < = 0 then wakeup(S,L); end 操 作 系 统 | 进 程 的 同 步 与 通 信 32 CUIT 徐虹 -1 -2 -1 操 作 系 统 | 进 程 的 同 步 与 通 信 33 CUIT 徐虹 ?信号量集机制 ?AND型信号量集机制 ?问题引入 进程A、B访问共享数据D和E 信号量Dmutex=1,Emutex=1 process A : wait(Dmutex) ; wait(Emutex) ; process B : wait(Emutex) ; wait(Dmutex) ; 操 作 系 统 | 进 程 的 同 步 与 通 信 34 CUIT 徐虹 A、B交替执行: process A : wait(Dmutex) ;则Dmutex = 0 process B : wait(Emutex) ; 则Emutex = 0 process A : wait(Emutex) ; 则Emutex = -1,阻塞 process B : wait(Dmutex) ; 则Dmutex = -1,阻塞 发生死锁。 操 作 系 统 | 进 程 的 同 步 与 通 信 35 CUIT 徐虹 ?AND同步机制的思想 Swait(S1,S2,…,Sn) If S1>=1 and S2>=1 and … and Sn>=1 then For i:=1 to n do Si := Si – 1; Endfor Else 将进程插入第i(Si<1)个等待队列。 设置该进程的程序计数器到Swait操作的开始。 Endif 操 作 系 统 | 进 程 的 同 步 与 通 信 36 CUIT 徐虹 Ssignal(S1,S2,…,Sn) For i:=1 to n do Si := Si + 1; 将所有等待Si资源的进程移到就绪队列。 Endfor 7 操 作 系 统 | 进 程 的 同 步 与 通 信 37 CUIT 徐虹 ?一般“信号量”机制 Swait(S1,t1,d1;…;Sn,tn,dn) If S1>=t1 and S2>=t2 and … and Sn>=tn then For i:=1 to n do Si := Si – di; Endfor Else 将执行进程插入第i(Si<ti)个等待队列。 设置该进程的程序计数器到Swait操作的开始。 Endif 操 作 系 统 | 进 程 的 同 步 与 通 信 38 CUIT 徐虹 Ssignal(S1,d1,…;Sn,dn) For i:=1 to n do Si := Si + di; 将所有等待Si资源的进程移到就绪队列。 Endfor 操 作 系 统 | 进 程 的 同 步 与 通 信 39 CUIT 徐虹 特例: ? Swait(S,d,d):一个信号量,可每次 申请d个资源; ? Swait(S,1,1):记录型信号量(S>1) 或互斥信号量(S=1); ? Swait(S,1,0):当S>=1,允许多个进 程进入某临界区;当S=0后,阻止任 何进程进入临界区。 操 作 系 统 | 进 程 的 同 步 与 通 信 40 CUIT 徐虹 3.3经典进程同步问题 ?同步的概念 例:计算进程和打印进程使用同一缓冲区Buf。 Pc:…… Pp:…… local Cbuf local PBuf A:Computer(Cbuf); B:While (Buf != NULL) While (CBuf!= NULL) Buf——> PBuf CBuf ——>Buf Print(PBuf); Goto A; Goto B; 操 作 系 统 | 进 程 的 同 步 与 通 信 41 CUIT 徐虹 ?异步环境 相互合作的一组并发进程,其中每 一进程都能以各自独立的,不可预知的 速度向前推进。 ?进程同步 相互合作的进程之间需要交换一定的 信息,当某进程未获得其合作进程发来的 信息之前,该进程等待,直到该信息到来 时才被唤醒继续执行,从而保证诸进程的 协调运行。 操 作 系 统 | 进 程 的 同 步 与 通 信 42 CUIT 徐虹 ?生产者—消费者问题 ?问题描述 通过由n个环形缓冲区构成的 缓冲池,把生产者P1,P2,……, PM和一群消费者C1,C2,……, CK联系起来。 ?算法描述 8 操 作 系 统 | 进 程 的 同 步 与 通 信 43 CUIT 徐虹 操 作 系 统 | 进 程 的 同 步 与 通 信 44 CUIT 徐虹 变量定义: begin Var mutex,empty,full:semaphore:=1,n,0; buffer:array[0,…,n-1] of item; in,out:integer := 0,0; 操 作 系 统 | 进 程 的 同 步 与 通 信 45 CUIT 徐虹 parbegin producer: begin repeat produce next product ; wait (empty); wait (mutex); buffer(in):=nextp ; in := (in+1) mod n ; signal (full); signal (mutex); until false ; end 操 作 系 统 | 进 程 的 同 步 与 通 信 46 CUIT 徐虹 consumer: begin repeat wait (full); wait (mutex); nextc := buffer(out); out := (out+1) mod n; signal (empty); signal (mutex); consume the item in nextc; until false ; end parend end 操 作 系 统 | 进 程 的 同 步 与 通 信 47 CUIT 徐虹 wait(empty) empty >= 0则数据送入缓冲区 empty < 0被阻塞(满) signal(empty) empty > 0 empty <= 0释放一个空缓冲区,唤醒一个 阻塞的生产者进程 操 作 系 统 | 进 程 的 同 步 与 通 信 48 CUIT 徐虹 wait(full) full >= 0则从缓冲区取走数据 full < 0被阻塞(空) signal(full) full > 0 full <= 0数据装入缓冲区,唤醒一个 阻塞的消费者进程 9 操 作 系 统 | 进 程 的 同 步 与 通 信 49 CUIT 徐虹 ?注意: ?互斥和同步信号量的原语必须成对 出现。 ?signal操作的次序无关紧要,但wait 操作的次序不能颠倒,否则会造成 死锁。 ?用AND信号量解决生产者——消 费者问题 操 作 系 统 | 进 程 的 同 步 与 通 信 50 CUIT 徐虹 ?读者——写者问题 读者进程 写者进程(互斥的存取共享对象) 解决读写问题,导致饥饿现象 public class readersandwriters { int readcount; semaphore x = new semaphore(1); semaphore wsem = new semaphore(1); 操 作 系 统 | 进 程 的 同 步 与 通 信 51 CUIT 徐虹 public void reader() { while (true) { wait (x); readcount++; if (readcount == 1) wait (wsem); signal (x); READUNIT(); wait (x); readcount--; if (readcount == 0) signal (wsem); signal (x); } } 操 作 系 统 | 进 程 的 同 步 与 通 信 52 CUIT 徐虹 public void writer() { while (true) { wait (wsem); WRITEUNIT(); signal (wsem); }} public static void main (String args[]) { readcount = 0; parbegin (reader, writer); } 操 作 系 统 | 进 程 的 同 步 与 通 信 53 CUIT 徐虹 ?哲学家进餐问题 操 作 系 统 | 进 程 的 同 步 与 通 信 54 CUIT 徐虹 public class diningphilosophers { semaphore [] fork = new semaphore[5](1); int i; 10 操 作 系 统 | 进 程 的 同 步 与 通 信 55 CUIT 徐虹 public void philosopher (int i) { while (true) { think(); wait (fork[i]); wait (fork [(i+1) % 5]); eat(); signal(fork [(i+1) % 5]); signal(fork[i]); }} 操 作 系 统 | 进 程 的 同 步 与 通 信 56 CUIT 徐虹 public static void main() { parbegin (philosopher (0), philosopher (1), philosopher (2), philosopher (3), philosopher (4)); } } 操 作 系 统 | 进 程 的 同 步 与 通 信 57 CUIT 徐虹 ?解决死锁的方法: ?至多允许四个哲学家同时进餐。 ?仅当哲学家的左右两支叉子均可用时, 才进餐。(用AND信号量机制解决哲 学家进餐问题。) ?奇数号哲学家先拿左边的叉子,偶数 号哲学家先拿右边的叉子。 操 作 系 统 | 进 程 的 同 步 与 通 信 58 CUIT 徐虹 3 . 4管程机制 ?管程的引入 ?管程的基本概念 ?管程的定义 ?条件变量 ?利用管程解决生产者——消费者 问题 操 作 系 统 | 进 程 的 同 步 与 通 信 59 CUIT 徐虹 操 作 系 统 | 进 程 的 同 步 与 通 信 60 CUIT 徐虹 ?利用管程解决哲学家进餐问题 Monitor forks; final static int N = 5; final static int LEFT = (i-1) mod N; final static int RIGHT = (i+1) mod N; final static int THINKING = 0; final static int HUNGRY = 1; final static int EATING = 2; 11 操 作 系 统 | 进 程 的 同 步 与 通 信 61 CUIT 徐虹 public class semaphore { int value; } int [] state = new int[N]; semaphore mutex = new semaphore(1); semaphore [] s = new semaphore[N](); 操 作 系 统 | 进 程 的 同 步 与 通 信 62 CUIT 徐虹 public void take_forks(int i) { wait(mutex); state[i] = HUNGRY; test(i); signal(mutex); if (state[i] = EATING) then wait(s[i]); } 操 作 系 统 | 进 程 的 同 步 与 通 信 63 CUIT 徐虹 public void put_forks(int i) { wait(mutex); state[i] = THINKING; test(LEFT); test(RIGHT); signal(mutex); } 操 作 系 统 | 进 程 的 同 步 与 通 信 64 CUIT 徐虹 void test(int i) { if (state[i] ==HUNGRY && state[LEFT] != EATING && state[RIGHT] !=EATING) { state[i] = EATING; signal(s[i]); } } 操 作 系 统 | 进 程 的 同 步 与 通 信 65 CUIT 徐虹 public void philosopher (int i) { while (true) { think( ); take_forks(i); eat( ); put_forks(i); } } 操 作 系 统 | 进 程 的 同 步 与 通 信 66 CUIT 徐虹 3.5进程通信 进程之间的信息交换称为进程通信。 低级通信和高级通信 ?共享存储系统(Shared-Memory System) ?基于共享数据结构的通信方式 ?基于共享存储区的通信方式 12 操 作 系 统 | 进 程 的 同 步 与 通 信 67 CUIT 徐虹 ?管道通信 管道(pipe)指用于连接一个读进程和一个 写进程,以实现它们之间通信的共享文件。 例:UNIX的管道 管道按FIFO方式单向传送消息。 写端fd[1] fd[0]读端 —————> pipe(fd) ————— > write (fd[1],buf,size) read(fd[0],buf,size) 操 作 系 统 | 进 程 的 同 步 与 通 信 68 CUIT 徐虹 例:建立一个管道,同时父进程生成一个子进程,子进 程向管道中写入一字符串,父进程从管道中读出字符 串。 #include <stdio.h> main() {int x,fd[2]; char buf[30],s[30]; pipe(fd); while ((x=fork()) == -1); if (x == 0) { sprintf(buf,”this is an example.\n”); write(fd[1],buf,30); exit(0); } else { wait(0); read(fd[0],s,30); printf(“%s”,s); } } 操 作 系 统 | 进 程 的 同 步 与 通 信 69 CUIT 徐虹 ?消息传递系统 ?通信链路(communication link) ?消息的格式 ?进程同步方式 操 作 系 统 | 进 程 的 同 步 与 通 信 70 CUIT 徐虹 操 作 系 统 | 进 程 的 同 步 与 通 信 71 CUIT 徐虹 ?信箱 当两个进程间需要通信时,由发送进程 创建一个与接收进程相连的信箱。发送进 程把消息送到信箱,接收进程从信箱中取 走消息。 信箱头:名称,大小,方向,进程名等; 信箱体:由若干格子组成,每一格存放一条 消息。 双向信箱 信箱溢出 操 作 系 统 | 进 程 的 同 步 与 通 信 72 CUIT 徐虹 13 操 作 系 统 | 进 程 的 同 步 与 通 信 73 CUIT 徐虹 ?信箱通讯原语 ?创建信箱原语 用来创建一个有格子数目和大小一定 的信箱,同时把允许该进程所创建的信 箱数目减1。 ?链接原语 信箱创建后,利用链接原语把信箱链接 在窗口上,断开链接是通过把信箱链接到 虚窗口上实现的。 操 作 系 统 | 进 程 的 同 步 与 通 信 74 CUIT 徐虹 ?发送原语 发送进程私用信号量fromnum,初值为格子数n; 接收进程私用信号量mesnum,初值为0; deposit(m) begin local x wait (fromnum) 选择空格x mailbox(x) ←m 置格x的标志为满 signal (mesnum) end 操 作 系 统 | 进 程 的 同 步 与 通 信 75 CUIT 徐虹 ?接收原语 receive(m) begin local x wait (mesnum) 选择满格x m ←mailbox(x) 置格x的标志为空 signal (fromnum) end 操 作 系 统 | 进 程 的 同 步 与 通 信 76 CUIT 徐虹 ?撤消信箱原语 信箱可在任何时候由它的拥有者 调用撤消原语予以撤消,同时将进程 所允许创建的信箱数加1,并将信箱 所占用的看见归还拥有者进程。 ?信箱类型 ?私用信箱:单向通信链路,由用户创建。 ?公用信箱:多输入/输出公用信箱,提 高效率。双向通信链路,由OS创建。 ?共享信箱:由进程创建。 ?发送进程和接受进程的对应关系 操 作 系 统 | 进 程 的 同 步 与 通 信 77 CUIT 徐虹 ?消息缓冲队列通信机制 ?通信原语 Send ( Receiver , message ) ; Receive ( Sender , message ) ; ?消息缓冲区 Type message buffer = record Sender Size Text Next end 操 作 系 统 | 进 程 的 同 步 与 通 信 78 CUIT 徐虹 ?用直接通信原语解决生产者——消费者问题 repeat produce an item in nextp ; Send ( consumer , nextp) ; Until false ; repeat receive ( producer , nextc ) ; consume the item in nextc ; Until false ; 14 操 作 系 统 | 进 程 的 同 步 与 通 信 79 CUIT 徐虹 ?PCB中有关通信的数据项 Type message buffer = record … mq mutex sm … end 操 作 系 统 | 进 程 的 同 步 与 通 信 80 CUIT 徐虹 ?发送原语 procedure send ( receiver , a ) begin getbuf ( a , size , I ) ; i.sender := a.sender ; i.size := a.size ; i.text := a.text ; i.next := 0 ; getid ( PCB set , receiver , j ) ; wait ( j.mutex ); insert ( j.mq.i ) ; signal ( j.mutex ) ; signal (j.sm ) ; end 操 作 系 统 | 进 程 的 同 步 与 通 信 81 CUIT 徐虹 ?接收原语 procedure receive ( b ) begin j := internal name ; wait ( j.sm ); wait ( j.mutex ); remove ( j.mq.i ) ; signal ( j.mutex ) ; b.sender := i.sender ; b.size := i.size ; b.text := i.text ; b.next := 0 ; end