作業系統程序同步三大問題:生產者消費者,哲學家進餐,讀者寫者問題
對於非科班出身的我,,,最近自學作業系統做了一些程序同步的筆記,寫出來,希望能對大家有幫助,我是參照哈工大張英濤老師的視訊和湯子瀛的書寫的:
程序與程序之間的合作機制:
訊號量機制!!!
訊號量是一種資料結構。
訊號量的值與相應資源的使用情況有關。
訊號量的值僅由原語P、V操作改變
(1)整型訊號量
整型數 S <=0 代表資源被佔用,不可用 S>0代表空閒可用
P操作(wait)原語
V操作(signal)原語
Wait(S) While S<=0 do no-op(無行為) S:=S-1; //可用啦,那我就用咯 |
Signal(S) S:=S+1; |
wait(S) 和signal(S)是原子操作。
缺點:只要訊號量小於等於0,不滿足讓權等待。就是不會先讓其進入阻塞,把CPU讓給別人用。
(2)記錄型訊號量(目前應用較廣)
記錄型結構,包含兩個資料項:
Type semaphore = record Value:integer; L:lise of process; end |
S.value為資源訊號量初值,是某類資源的數目;
value大於0 的時候代表可用資源數目
value<0時候,絕對值代表等待使用資源的程序個數。也就是扔進阻塞佇列。
wait操作申請某一個單位資源
Procedure wait(s) Var S:semaphore; Begin S.value:=S.value-1; If S.value<0 then block(s,L); End |
signal操作:釋放一個資源
Procedure signal(s) Var S:semaphore; Begin S.value = S.value+1; If S.value<=0 then wakeup(s,L) //這裡小於0的話,說明還有程序在等待。 end |
當S.value=1則是訪問臨界資源,是互斥訊號量。
死鎖現象:
Pa pb
…… ……
wait(Dmutex) wait(Emutex)
wait(Emutex) wait(Dmutex)
…… ……
(3)AND型訊號量
基本思想:將所需的資源一次性全部獲取,使用後一次性全部釋放。
Swait(s1,s2,..,sn) If s1>=1 and ... and sn>=1 then For i:=1 to n do Si:= si-1; End for Else 當發現第一個Si<1就把該程序放入阻塞等待佇列,並將其程式計數器至於Swait操作的開始位置(也就是把下一條指令改下。) End if |
Ssignal(s1,s2,..,s3) For i:1 to n do Si := si +1; 將所有等待si的程序由等待佇列取出放到就緒佇列 End for |
用訊號量能實現互斥:兩個程序程式碼見下面
Var mutex:semaphore:=1; Begin Parbegin //從部分開始的程式碼 Process1:begin Repeat Wait(mutex); // P操作 訊號量mutex Critical section Signal(mutex) Remainder section Until false; End Process2:begin Repeat Wait(mutex); Critical section Signal(mutex); Reminder section Until false; end |
注意點:
wait(mutex) 和 signal(mutex) 必須成對出現。
經典的同步問題!!!!!!!!!!!!!!!
1、生產者-消費者問題
2、讀者-寫者問題
3、哲學家進餐問題
生產者-消費者問題 一組生產者程序生產產品給一組消費者消費。為使他們併發執行,設有一個n個緩衝區的緩衝池,生產者一次向一個緩衝區投入訊息,消費者從一個緩衝區中取得訊息。這是兩個程序相互合作的模型。! 制約關係: (1)不允許消費者到一個空緩衝區中取資料 (2)不允許生產者向一個已滿的緩衝區投入資料。 |
例如:輸入時,輸入程序是生產者,計算程序是消費者
輸出時,計算程序是生產者,列印程序是消費者
這裡用記錄型訊號量來解決生產者-消費者問題。區分下互斥量和訊號量
(什麼是記錄型?用一個數據結構來表示,整型表示使用資源和等待程序數,隊列表示阻塞佇列)
(1)設有n個緩衝區,每個緩衝區存放一個訊息,用互斥量mutex對緩衝池實現互斥訪問。
所以這裡第一個訊號量是用來作為互斥用的,初值是1;;;;這是使得投入和取出這兩個動作時互斥的,不能同時進行。!!!!
(2)用資源訊號量empty和full分別表示緩衝池中空緩衝區及滿緩衝區的數量。所以這裡資源訊號量是用來同步用的,empty初值是n,full初值是0。
Var mutex:semapjore:=1; Empty:semphore:=n; Full:semaphore:=0; Buffer:array[0,...,n-1] of item; // 這裡用陣列形式,可以指明下一個資料存放位置用迴環就好 In,out:integer:=0,0; //放入和取出的資料地址,就是陣列的兩個下表指向。 Procedure:begin Repeat …… Procedure an item nextp; // 生產下一個產品,就是產生一個數據 …… Wait(empty); // 申請成功則empty減一了唄 Wait(mutex); Buffer(in):=nextp; In:=(in+1) mod n; Signal(mutex); Signal(full); Until false; End Consumer:begin Repeat Wait(full) Wait(mutex) Nextc := buffer(out); Out :=(out+1) mod n; Signal(mutex); Signal(empty); Consumer the item in nextc; //對應的,也有一個消費過程 Until false; End; |
注意點:
1:wait(mutex)和signal(mutex)必須成對出現。不過這裡訊號量的出現不是在同一個程序當中就是了。
2:這裡mutex和empty full必須用mutex的型別變數把。不然也會出現同步問題。
3:這裡P操作是不能顛倒的,否則會進入死鎖,比如生產者應該先獲取empty,因為在消費者那裡也會對empty進行操作,。。所以一定要先鎖定互斥量,再進行訊號量,V操作倒是沒有順序要求。
2、哲學家進餐問題
有五個哲學家,他們的生活方式是交替進行思考和進餐。 哲學家們共用一個圓桌,有五張椅子,五個碗,五隻筷子,所以一個科學家想吃飯應該同時拿起最近的左右筷子吃。所以要協調他們吃飯時間。 |
分析:筷子就是臨界資源!在一段時間內只能有一個科學家使用。用一個訊號量表示一個筷子,五個筷子就共有五個訊號量,
var chopstick: array[0...4] of semaphore
每個訊號量初始化為1;
用記錄型訊號量來解決
設第i個哲學家
Repeat Wait(chopstick[i]); Wait(chopstick[(i+1)mod 5]); // 由於這五個筷子是同個地位的,所以取哪個先都可以 Eat... Signal(chopstick[i]); Signal(chopstick[(i+1)mod 5]); Think; Until false; end |
但是,當五個哲學家同時飢餓,那麼五個人同時拿起左邊筷子,那麼五個人就無限等待下去了。。。這就死鎖了。
解決辦法:
(1)至多四個人拿起左邊筷子。。保證至少有一個人可以用餐,那麼就能解決了。
(2)僅當左右兩個筷子同時拿起才能進餐 類似AND型訊號量
(3)規定奇數號的先拿起左邊的,偶數號拿右邊。。
用AND型訊號量來解決哲學家問題:同一組人使用同一些東西。
var chopstick:array[0...4] ofsemaphore:={1,1,1,1,1};
Repeat Think; Swait( Chopstick[i] and chopstick[(i+1)mod 5]); Eat; Ssignal(Chopstick[i] and chopstick[(i+1)mod 5]); Think; Until false; end |
(3)讀者-寫者問題
一個數據檔案或記錄可被多個程序共享,其中,有些程序要求讀,有些程序則要求寫或修改。 把只要求讀的程序 Reader程序 要求寫修改的是Writer程序。 允許多個讀者程序同時共享一個物件,不允許一個writer程序和多個Reader程序或Writer程序同時訪問共享物件。 |
這裡的共享物件不是臨界資源,因為允許多個程序訪問---讀!
(1)為解決一個writer程序和reader程序互斥,設定一個互斥訊號量Wmutex;
(2)設定一個整型變數Reader count表示正在讀的程序數目。
(3)ReaderCount是可以被多個程序訪問的臨界資源,為它設定互斥訊號量Rmutex;
(4)僅當Readercount = 0,表示無reader程序在讀,Reader程序執行P操作。若P操作成功,Reader程序便可讀,使得Readercount+1;其他Readercount的值就不需要P操作了。
記住Rmutex是為了保證多個執行緒訪問Readercount個數同步問題才建立的!!
Var Rmutex,Wmutex:semaphore:= 1,1; Readercount:integer:=0; Begin Parbegin Reader:begin Repeat Wait(Rmutex); If (Readercount=0 then Wait(Wmutex)); Readercount++; Signal(Rmutex); Perform read operation; Wait(Rmutex); Readercount:=Readercount-1; If Readercount==0 then signal(Wmutex); Signal(Rmutex); Until false; End Writer:begin Repeat Wait(Wmutex); Perform write operation; Signal(Wmutex); Until false; End Parend end |