进程之间的同步机制
多進程的系統中避免不了進程間的相互關系。本講將介紹進程間的兩種主要關系——同步與互斥,然后著重講解解決進程同步的幾種機制。?
????? 進程互斥是進程之間發生的一種間接性作用,一般是程序不希望的。通常的情況是兩個或兩個以上的進程需要同時訪問某個共享變量。我們一般將發生能夠問共享變量的程序段稱為臨界區。兩個進程不能同時進入臨界區,否則就會導致數據的不一致,產生與時間有關的錯誤。解決互斥問題應該滿足互斥和公平兩個原則,即任意時刻只能允許一個進程處于同一共享變量的臨界區,而且不能讓任一進程無限期地等待?;コ鈫栴}可以用硬件方法解決,我們不作展開;也可以用軟件方法,這將會在本講詳細介紹。?
????? 進程同步是進程之間直接的相互作用,是合作進程間有意識的行為,典型的例子是公共汽車上司機與售票員的合作。只有當售票員關門之后司機才能啟動車輛,只有司機停車之后售票員才能開車門。司機和售票員的行動需要一定的協調。同樣地,兩個進程之間有時也有這樣的依賴關系,因此我們也要有一定的同步機制保證它們的執行次序。?
本講主要介紹以下四種同步和互斥機制:信號量、管程、會合、分布式系統。
一,信號量
參考自http://blog.csdn.net/leves1989/article/details/3305609
理解PV:
PV操作由P操作原語和V操作原語組成(原語是不可中斷的過程),對信號量進行操作,具體定義如下:
????P(S):①將信號量S的值減1,即S=S-1;
???????????②如果S30,則該進程繼續執行;否則該進程置為等待狀態,排入等待隊列。
????V(S):①將信號量S的值加1,即S=S+1;
???????????②如果S>0,則該進程繼續執行;否則釋放隊列中第一個等待信號量的進程。
PV操作的意義:我們用信號量及PV操作來實現進程的同步和互斥。PV操作屬于進程的低級通信。
什么是信號量?信號量(semaphore)的數據結構為一個值和一個指針,指針指向等待該信號量的下一個進程。信號量的值與相應資源的使用情況有關。當它的值大于0時,表示當前可用資源的數量;當它的值小于0時,其絕對值表示等待使用該資源的進程個數。注意,信號量的值僅能由PV操作來改變。
???? 一般來說,信號量S30時,S表示可用資源的數量。執行一次P操作意味著請求分配一個單位資源,因此S的值減1;當S<0時,表示已經沒有可用資源,請求者必須等待別的進程釋放該類資源,它才能運行下去。而執行一個V操作意味著釋放一個單位資源,因此S的值加1;若S£0,表示有某些進程正在等待該資源,因此要喚醒一個等待狀態的進程,使之運行下去。
利用信號量和PV操作實現進程互斥的一般模型是:
進程P1????????????? 進程P2?????????? ……????????? 進程Pn
……????????????????? ……?????????????????????????? ……
P(S);????????????? P(S);???????????????????????? P(S);
臨界區;???????????? 臨界區;??????????????????????? 臨界區;
V(S);????????????? V(S);??????????????????????? V(S);
……????????????????? ……??????????? ……?????????? ……
????其中信號量S用于互斥,初值為1。
????使用PV操作實現進程互斥時應該注意的是:
????(1)每個程序中用戶實現互斥的P、V操作必須成對出現,先做P操作,進臨界區,后做V操作,出臨界區。若有多個分支,要認真檢查其成對性。
????(2)P、V操作應分別緊靠臨界區的頭尾部,臨界區的代碼應盡可能短,不能有死循環。
?? (3)互斥信號量的初值一般為1。
利用信號量和PV操作實現進程同步
PV操作是典型的同步機制之一。用一個信號量與一個消息聯系起來,當信號量的值為0時,表示期望的消息尚未產生;當信號量的值非0時,表示期望的消息已經存在。用PV操作實現進程同步時,調用P操作測試消息是否到達,調用V操作發送消息。
????使用PV操作實現進程同步時應該注意的是:
????(1)分析進程間的制約關系,確定信號量種類。在保持進程間有正確的同步關系情況下,哪個進程先執行,哪些進程后執行,彼此間通過什么資源(信號量)進行協調,從而明確要設置哪些信號量。
????(2)信號量的初值與相應資源的數量有關,也與P、V操作在程序代碼中出現的位置有關。
????(3)同一信號量的P、V操作要成對出現,但它們分別在不同的進程代碼中。
【例1】生產者-消費者問題
在多道程序環境下,進程同步是一個十分重要又令人感興趣的問題,而生產者-消費者問題是其中一個有代表性的進程同步問題。下面我們給出了各種情況下的生產者-消費者問題,深入地分析和透徹地理解這個例子,對于全面解決操作系統內的同步、互斥問題將有很大幫助。
(1)一個生產者,一個消費者,公用一個緩沖區。
定義兩個同步信號量:
empty——表示緩沖區是否為空,初值為1。
???full——表示緩沖區中是否為滿,初值為0。
生產者進程
while(TRUE){
生產一個產品;
?????P(empty);
?????產品送往Buffer;
?????V(full);
}
消費者進程
while(True){
P(full);
???從Buffer取出一個產品;
???V(empty);
???消費該產品;
???}
(2)一個生產者,一個消費者,公用n個環形緩沖區。
定義兩個同步信號量:
empty——表示緩沖區是否為空,初值為n。
full——表示緩沖區中是否為滿,初值為0。
??? 設緩沖區的編號為1~n-1,定義兩個指針in和out,分別是生產者進程和消費者進程使用的指
,指向下一個可用的緩沖區。
生產者進程
while(TRUE){
?????生產一個產品;
?????P(empty);
?????產品送往buffer(in);
?????in=(in+1)mod n;
?????V(full);
}
消費者進程
while(TRUE){
P(full);
???從buffer(out)中取出產品;
???out=(out+1)mod n;
???V(empty);
???消費該產品;
???}
(3)一組生產者,一組消費者,公用n個環形緩沖區
????在這個問題中,不僅生產者與消費者之間要同步,而且各個生產者之間、各個消費者之間還必須互斥地訪問緩沖區。
定義四個信號量:
empty——表示緩沖區是否為空,初值為n。
full——表示緩沖區中是否為滿,初值為0。
mutex1——生產者之間的互斥信號量,初值為1。
mutex2——消費者之間的互斥信號量,初值為1。
????設緩沖區的編號為1~n-1,定義兩個指針in和out,分別是生產者進程和消費者進程使用的指針,指向下一個可用的緩沖區。
生產者進程
while(TRUE){
?????生產一個產品;
?????P(empty);
?????P(mutex1);
?????產品送往buffer(in);
?????in=(in+1)mod n;
?????V(mutex1);
?????V(full);
}
消費者進程
while(TRUE){
P(full)
???P(mutex2);
???從buffer(out)中取出產品;
???out=(out+1)mod n;
???V(mutex2);
???V(empty);
???消費該產品;
???}
? 需要注意的是無論在生產者進程中還是在消費者進程中,兩個P操作的次序不能顛倒。應先執行同步信號量的P操作,然后再執行互斥信號量的P操作,否則可能造成進程死鎖。
【例2】桌上有一空盤,允許存放一只水果。爸爸可向盤中放蘋果,也可向盤中放桔子,兒子專等吃盤中的桔子,女兒專等吃盤中的蘋果。規定當盤空時一次只能放一只水果供吃者取用,請用P、V原語實現爸爸、兒子、女兒三個并發進程的同步。
分析?在本題中,爸爸、兒子、女兒共用一個盤子,盤中一次只能放一個水果。當盤子為空時,爸爸可將一個水果放入果盤中。若放入果盤中的是桔子,則允許兒子吃,女兒必須等待;若放入果盤中的是蘋果,則允許女兒吃,兒子必須等待。本題實際上是生產者-消費者問題的一種變形。這里,生產者放入緩沖區的產品有兩類,消費者也有兩類,每類消費者只消費其中固定的一類產品。
????解:在本題中,應設置三個信號量S、So、Sa,信號量S表示盤子是否為空,其初值為l;信號量So表示盤中是否有桔子,其初值為0;信號量Sa表示盤中是否有蘋果,其初值為0。同步描述如下:
int S=1;
int Sa=0;
int So=0;
??????main()
??????{
????????cobegin
????????????father();????? /*父親進程*/
????????????son();??????? /*兒子進程*/
??????????? daughter();??? /*女兒進程*/
????????coend
????}
???father()
????{
????????while(1)
??????????{
????????????P(S);
????????????將水果放入盤中;
????????????if(放入的是桔子)V(So);
????????????else? V(Sa);
???????????}
?????}
????son()
????{
????????while(1)
??????????{
?????????????P(So);
?????????????從盤中取出桔子;
???????????? V(S);
?????????????吃桔子;
????????????}
????}
????daughter()
????{
?????????while(1)
????????????{
??????????????P(Sa);
??????????????從盤中取出蘋果;
??????????????V(S);
??????????????吃蘋果;
????????????}
}
思考題:
四個進程A、B、C、D都要讀一個共享文件F,系統允許多個進程同時讀文件F。但限制是進程A和進程C不能同時讀文件F,進程B和進程D也不能同時讀文件F。為了使這四個進程并發執行時能按系統要求使用文件,現用PV操作進行管理,請回答下面的問題:
????(1)應定義的信號量及初值:????????????????????。
????(2)在下列的程序中填上適當的P、V操作,以保證它們能正確并發工作:
?????A()??????????????? B()????????????????? C()???????????????? D()
??????{???????????????? {??????????????????? {????????????????? {
??????[1];??????????????? [3];????????????????? [5];???????????????? [7];
??????read F;???????????? read F;??????????????? read F;????????????? read F;
?????[2];??????????????? [4];????????????????? [6];???????????????? [8];
??????}????????????????? }??????????????????? }????????????????? }?
??? 思考題解答:
(1)定義二個信號量S1、S2,初值均為1,即:S1=1,S2=1。其中進程A和C使用信號量S1,進程B和D使用信號量S2。
(2)從[1]到[8]分別為:P(S1) V(S1) P(S2) V(S2) P(S1) V(S1) P(S2) V(S2)
二,管程:參考自http://hi.baidu.com/zucenaa/blog/item/e63d22277c9d9c09918f9de2.html
信號量機制功能強大,但使用時對信號量的操作分散,而且難以控制,讀寫和維護都很困難。因此后來又提出了一種集中式同步進程——管程。其基本思想是將共享變量和對它們的操作集中在一個模塊中,操作系統或并發程序就由這樣的模塊構成。這樣模塊之間聯系清晰,便于維護和修改,易于保證正確性。
管程作為一個模塊,它的類型定義如下:?
monitor_name = MoNITOR;?
共享變量說明;?
define 本管程內部定義、外部可調用的函數名表;?
use 本管程外部定義、內部可調用的函數名表;?
內部定義的函數說明和函數體?
{?
共享變量初始化語句;?
}
從語言的角度看,管程主要有以下特性:?
(1)模塊化。管程是一個基本程序單位,可以單獨編譯;?
(2)抽象數據類型。管程是中不僅有數據,而且有對數據的操作;?
(3)信息掩蔽。管程外可以調用管程內部定義的一些函數,但函數的具體實現外部不可見;?
對于管程中定義的共享變量的所有操作都局限在管程中,外部只能通過調用管程的某些函數來間接訪問這些變量。因此管程有很好的封裝性。?
為了保證共享變量的數據一致性,管程應互斥使用。 管程通常是用于管理資源的,因此管程中有進程等待隊列和相應的等待和喚醒操作。在管程入口有一個等待隊列,稱為入口等待隊列。當一個已進入管程的進程等待時,就釋放管程的互斥使用權;當已進入管程的一個進程喚醒另一個進程時,兩者必須有一個退出或停止使用管程。在管程內部,由于執行喚醒操作,可能存在多個等待進程(等待使用管程),稱為緊急等待隊列,它的優先級高于入口等待隊列。?
因此,一個進程進入管程之前要先申請,一般由管程提供一個enter過程;離開時釋放使用權,如果緊急等待隊列不空,則喚醒第一個等待者,一般也由管程提供外部過程leave。?
管程內部有自己的等待機制。管程可以說明一種特殊的條件型變量:var c:condition;實際上是一個指針,指向一個等待該條件的PCB隊列。對條件型變量可執行wait和signal操作:(聯系P和V; take和give)?
wait(c):若緊急等待隊列不空,喚醒第一個等待者,否則釋放管程使用權。執行本操作的進程進入C隊列尾部;?
signal(c):若C隊列為空,繼續原進程,否則喚醒隊列第一個等待者,自己進入緊急等待隊列尾部。
【示例】
| 生產者-消費者問題(有buffer) |
| 問題描述:(一個倉庫可以存放K件物品。生產者每生產一件產品,將產品放入倉庫,倉庫滿了就停止生產。消費者每次從倉庫中去一件物品,然后進行消費,倉庫空時就停止消費。? count,in,out: integer; ? //?count記錄共有幾件物品,in記錄第一個空緩沖區,out記錄第一個不空的緩沖區? buf:array [0..k-1] of item_type;? define deposit,fetch;? use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
|
【附】有關wait和signal的語言表示
信號量結構使用C語言表示如下:
wait()信號量部分代碼如下:
signal()信號量部分代碼如下:
一、The Bounded-Buffer Problem:
full初始化為0,empty初始化為n,mutex為1
二、The Readers-Writers Problem:
wrt初始化為1,readcount初始化為0,mutex為1
寫者操作:
讀者操作:
三、The Dining-Philosophers Problem:
所有的chopstick[5]全部初始化為1
但是這個解決方案的最大問題在于它會出現死鎖。所以我認為應該增加一個信號量mutex,并初始化為1:
這樣由于確保了一位哲學家在拿起兩只筷子的時間內其他哲學家不可以拿起任何一支筷子,從而破壞了死鎖出現需要的四個特征中的Hold And Wait特征,從而避免了死鎖的發生。
轉自:http://www.cnblogs.com/sonic4x/archive/2011/07/05/2098036.html
總結
- 上一篇: 西门子 S7-200CN CPU 224
- 下一篇: 吃鸡游戏计算机配置,运行端游吃鸡要什么配