本文地址:http://www.CUOXin.com/archimedes/p/classic-PRocess-synchronization-problems.html,轉載請注明源地址。
管程機制用信號量機制實現進程間的同步和互斥,既方便又有效。但存在以下兩個問題:
每個訪問臨界資源的進程都必須自備同步操作(P、V操作),這使大量的同步操作分散在各個進程中,給系統的管理帶來麻煩。
會因同步操作使用不當而導致系統死鎖。
解決方法:
管程(Monitors)
Dijkstra于1971年提出:為每個共享資源設立一個“秘書”來管理對它的訪問。 一切來訪者都要通過秘書,而秘書每次僅允許一個來訪者(進程)來訪問共享資源。這樣既便于系統管理共享資源,又能保證進程的互斥訪問和同步。1973年,Hanson和Hoare又把“秘書”概念發展為管程概念。
基本思想
系統中的各種軟硬件資源,其特性都可用數據結構抽象地描述,把對該共享數據結構實施的操作定義為一組過程。
進程對共享資源的訪問都是通過這組過程對共享數據結構的操作來實現的,這組過程可以根據資源的使用情況,接受或阻塞進程的訪問,確保每次只有一個進程使用共享資源,
實現進程的互斥。
管程的定義
一個數據結構和在該數據結構上能被并發進程所執行的一組操作,這組操作能同步進程和改變管程中的數據。如下圖所示:
管程的結構
TYPE <管程名> = MONITOR
<共享變量說明>;
procedure<過程名>(<形式參數表>);
begin
<過程體>;
end;
……
procedure <過程名>(<形式參數表>);
begin
<過程體>;
end;
begin
<管程的局部數據初始化語句序列>;
end;
管程相當于圍墻,它把共享變量和對它進行操作的若干過程圍了起來,所有進程要訪問臨界資源時,都必須經過管程才能進入,而管程每次只允許一個進程進入管程,從而實現進程互斥。
管程的特性
模塊化:是一個基本程序單位,可單獨編譯
抽象數據類型:管程中不僅有數據,而且有對數據的操作
信息隱蔽:數據結構和過程的具體實現外部不可見
管程與進程作比較
管程定義的是公用數據結構,而進程定義的是私有數據結構PCB;
管程把共享變量上的同步操作集中起來,而臨界區卻分散在每個進程中;
管程是為管理共享資源而建立的,進程主要是為占有系統資源和實現系統并發性而引入的;
管程是被要使用共享資源的進程所調用的,管程和調用它的進程不能并發工作,而進程之間能并發工作,并發性是進程的固有特性;
管程是OS中一個資源管理模塊,供進程調用;而進程有生命周期,由創建而產生、由撤銷而消亡
經典進程的同步問題在多道程序環境下,進程同步問題十分重要,出現一系列經典的進程同步問題,其中有代表性有:
生產者—消費者問題
哲學家進餐問題
讀者—寫者問題
問題描述:
“生產者---消費者”問題是最著名的進程同步問題。它描述了一組生產者向一組消費者提供產品,它們共享一個緩沖池(有n個緩沖區),生產者向其中投放產品,消費者從中取得產品。
它是許多相互合作進程的抽象,如輸入進程與計算進程;計算進程與打印進程等。
一個生產者,一個消費者,公用一個緩沖區定義兩個同步信號量:
empty——表示緩沖區是否為空,初值為1。
full——表示緩沖區中是否為滿,初值為0。
//生產者進程:while(TRUE){ 生產一個產品; P(empty); 把產品送往Buffer; V(full);}//消費者進程:while(TRUE){ P(full); 從Buffer取出一個產品; V(empty); 消費產品;}
擴展:M個生產者,K個消費者,公用有n個緩沖區的緩沖池
設緩沖池的長度為n(n>0),一群生產者進程P1,P2,…,Pm,一群消費者進程C1,C2,…,Ck,如圖所示。假定生產者和消費者是相互等效,只要緩沖池未滿,生產者就可以把產品送入緩沖區,類似地,只要緩沖池未空,消費者便可以從緩沖區取走產品并消耗它。生產者和消費者的同步關系將禁止生產者向滿的緩沖池輸送產品,也禁止消費者從空的緩沖池提取產品。
empty:說明空緩沖區的數目,其初值為緩沖池的大小n,表示消費者已把緩沖池中全部產品取走,有n個空緩沖區可用。
full:說明滿緩沖區的數目(即產品數目),其初值為0,表示生產者尚未把產品放入緩沖池,有0個滿緩沖區可用。
mutex: 說明該緩沖池是一臨界資源,必須互斥使用,其初值為1。
“生產者—消費者”問題的同步算法描述:
semaphore full=0; /*表示滿緩沖區的數目*/semaphore empty=n; /*表示空緩沖區的數目*/semaphore mutex=1; /*表示對緩沖池進行操作的互斥信號量*/main(){ cobegin producer(); consumer(); coend }producer(){ while(true) { 生產一個產品放入nextp; P(empty); P(mutex); Buffer[in]=nextp; in=(in+1) mod n; V(mutex); V(full); }}consumer(){ while(true) { P(full); P(mutex); nextc =Buffer[out]; out=(out+1) mod n; V(mutex); V(empty); 消費nextc中的產品; }}
“生產者-消費者”問題中應注意:
1. 互斥信號量的P、V操作在每一進程中必須成對出現。
2. 對資源信號量(full,empty)的P、V操作也必須成對出現,但可分別處于不同的程序中。
3. 多個P操作順序不能顛倒。
4. 先執行資源信號量的P操作,再執行互斥信號量的P操作,否則可能引起進程死鎖。
5. 它是一個同步問題:
(1)消費者想要取產品,緩沖池中至少有一個緩沖區是滿的。
(2)生產者想要放產品,緩沖池中至少有一個緩沖區是空的。
6. 它是一互斥問題
緩沖池是臨界資源,因此,各生產者進程和各消費者進程必須互斥訪問。
用管程機制解決生產者—消費者問題
1、建立Producer-consumer(PC)管程
Type PC=monitor var in,out,count:integer; buffer:array[0,…,n-1] of item; notfull,notempty:condition; put(item); get(item); begin in:=out:=0; /* 初始化代碼*/ count:=0; end
管程中的兩個條件變量:
(1) notfull 表示等待未滿緩沖區(空緩沖區)。
(2)notempty 表示等待未空緩沖區(滿緩沖區)。
1、建立Producer-consumer(PC)管程
put(item)過程生產者利用此過程將自己生產的產品放到緩沖池中,若發現緩沖池已滿(count n),則等待。
get(item)過程消費者利用此過程將緩沖池中的產品取走,若發現緩沖池已空(count 0),則等待。
put(item)Procedure entry put(item) begin if count >= n then notfull.wait; buffer(in):=nextp; in:=(in+1) mod n; count:=count+1; if notempty.queue then notempty.signal; endget(item)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
2、生產者—消費者問題的解決
Producer:begin repeat produce an item in nextp; PC.put(item); until false endConsumer:begin repeat PC.get(item); consume the item in nextc; until false end二、“哲學家進餐”問題
有五個哲學家,他們的生活方式是交替地進行思考和進餐。他們共用一張圓桌,分別坐在五張椅子上。在圓桌上有五個碗和五支筷子,平時一個哲學家進行思考,饑餓時便試圖取用其左、右最靠近他的筷子,只有在他拿到兩支筷子時才能進餐。進餐畢,放下筷子又繼續思考。
哲學家進餐問題可看作是并發進程并發執行時,處理共享資源的一個有代表性的問題。哲學家進餐問題的解決:
semaphore stick[5]={1,1,1,1,1}; /*分別表示5支筷子*/main(){ cobegin philosopher(0); philosopher(1); philosopher(2); philosopher(3); philosopher(4); coend }//第i個哲學家的活動算法philosopher(int i){ while(true) { 思考; P(stick[i]); P(stick[(i+1) mod 5]); 進餐; V(stick[i]); V(stick[(i+1) mod 5]); }}
說明:
1、此算法可以保證不會有相鄰的兩位哲學家同時進餐。
2、若五位哲學家同時饑餓而各自拿起了左邊的筷子,這使五個信號量stick均為0,當他們試圖去拿起右邊的筷子時,都將因無筷子而無限期地等待下去,即可能會引起死鎖。
上述解法可能出現永遠等待,有若干種辦法可避免死鎖:
至多允許四個哲學家同時進餐(解決方法一);
奇數號先取左手邊的筷子,偶數號先取右手邊的筷子(解決方法二);
每個哲學家取到手邊的兩把筷子才進餐,否則一把筷子也不?。ń鉀Q方法三)。
解決的方法(一)
設置一個信號量Sm,其初值為4,用于限制同時進餐的哲學家數目至多為4,這樣,第i個哲學家的活動可描述為:while(true) { signal(stick[i]); wait(Sm); signal(stick[(i+1) mod 5]); wait(stick[i]); signal(Sm); wait (stick[(i+1) mod 5]); …... …… think; eat; } …...
解決的方法(二)
while(true) signal(stick[i]); {if odd(i) signal (stick[(i+1) mod 5]); {wait(stick[i]); …... wait (stick[(i+1) mod 5]); think; } …... else } {wait (stick[(i+1) mod 5]); wait(stick[i]); } …… eat; …...
對5個哲學家,假設規定:單號者進餐時,先拿左手(i)的筷子,然后再拿右手(i+1)的筷子。雙號則先右后左。這樣既可使5個人同時進餐,又不致產生死鎖。
解決的方法(三)利用AND信號量機制解決哲學家進餐問題
semaphore stick[5]={1,1,1,1,1};philosopher(int i){ while(true) { think; Swait(stick[(i+1) mod 5],stick[i]); eat; Ssignal(stick[(i+1) mod 5],stick[i]); }}三、“讀者—寫者”問題問題描述:
一個數據對象(數據文件或記錄)可被多個進程共享。其中,讀者(reader)進程要求讀,寫者(writer)進程要求寫或修改。
為了保證讀寫的正確性和數據對象的一致性,系統要求:當有讀者進程讀文件時,不允許任何寫者進程寫,但允許多個讀者同時讀;當有寫者進程寫時,不允許任何其它寫者進程寫,也不允許任何讀者進程讀。
“讀者—寫者”問題的同步算法描述設置一個共享變量和兩個信號量:
共享變量readcount:記錄當前正在讀數據集的讀進程數目,初值為0。
讀互斥信號量rmutex :表示讀進程互斥地訪問共享變量readcount,初值為1.
寫互斥信號量wmutex:表示寫進程與其它進程(讀、寫)互斥地訪問數據集,初值為1.
“讀者—寫者”問題的同步算法描述
semaphore rmutex=1; semaphore wmutex=1; int readcount=0; main(){ cobegin reader(); writer(); coend}reader(){ while(true) { P(rmutex); if(readcount= =0) P(wmutex);/*第一位讀者阻止寫者*/ readcount++; //修改readcount V(rmutex); 讀數據集; P(rmutex); readcount--; //修改readcount if(readcount= =0) V(wmutex);/*第末位讀者允許寫者進*/ V(rmutex); }}writer(){ while(true) { P(wmutex); // 阻止其它進程(讀、寫); 寫數據集; V(wmutex); // 允許其它進程(讀、寫); }}參考資料《華東理工大學 操作系統》
新聞熱點
疑難解答