進程同步、進程互斥的兩種機制,這裏簡單總結是其中的信號量機制(Semaphores)。數據結構
建議: 很多概念涉及到進程同步的內容,因此查看這個內容時能夠結合或提早參考進程同步的內容,併發
信號量機制是 荷蘭學者 Dijkstra 提出的,這是一種卓有成效的進程同步工具。發展:整型信號量->記錄型信號量->AND 型信號量->信號量集,依次講解下。工具
定義:把整型信號量定義爲一個用於表示資源數目的整型量 S,它與通常整型 量不一樣,除初始化外,僅能經過兩個標準的原子操做(Atomic Operation) wait(S)和 signal(S)來訪問。this
PV操做由P操做原語和V操做原語組成(原語是執行時不可中斷,PV操做即定義中的原子操做wait(S)和signal(S))。所以這個操做一般被叫作PV操做。spa
注:在荷蘭文中,經過叫passeren,釋放叫vrijgeven,PV操做所以得名。wait(S)即申請資源,signal(S)釋放資源。指針
描述:code
wait(S) { while(S <= 0); S--; } signal(S) { S++; }
不足:當S<=0時,就要不斷檢測。陷入「忙等」狀態,不符合讓權等待。(在進程同步總結中有詳細說明)blog
在整型信號量基礎上,增長了一個進程鏈表指針L,連接全部等待的進程。 隊列
數據結構定義描述以下:一個整型變量value表示資源數目,進程鏈表指針L。進程
typedef struct{ int value; struct process *L; } semaphore;
相應的,wait(S)和signal(S)操做過程就是:
當wait(S)申請資源時,S.value減1(爲負數時的絕對值即等待進程的數目)。若S.value<0時表示資源耗盡了,進程使用block原句 自我阻塞,放棄處理機,插入到等待進程鏈表中S.L。這就符合了讓權等待,避免了「忙等」。
當signal(S)釋放資源,S.value加1。若加 1 後還是 S.value≤0,則表示在該鏈表L中,仍有等待該資源的進程被阻塞,故還應 調用 wakeup 原語,將 S.L 鏈表中的第一個等待進程喚醒。
若是 S.value 的初值爲 1,表示只容許一個進程訪問臨界資源,此時的信號量轉化爲互斥信號量,用於進程互斥。
void wait(semaphore S) { S.value--; if(S.value<0) { block(S.L); } } void signal(semaphore S) { S.value++; if(S.value<=0){ wakeup(S.L); } }
不足:同整型信號量同樣,都是正對的是共享一種臨界資源。 若一個進程須要兩個或更多資源後纔可執行,就會出現死鎖的可能,共享資源越多,進程死鎖可能性越大。
好比:有兩個進程A、B。兩個臨界資源D、E,互斥信號量(初值爲1)分別是Dmutex、Emutex。
按下面執行次序,A得到了D等待E,B得到了E等待D,就處於了僵持狀態,無外界干預,A、B就陷入了死鎖狀態。共享資源越多,進程死鎖可能越大。
process A: wait(Dmutex); 因而 Dmutex=0
process B: wait(Emutex); 因而 Emutex=0
process A: wait(Emutex); 因而 Emutex=-1 A 阻塞
process B: wait(Dmutex); 因而 Dmutex=-1 B 阻塞
如上所述,整型信號量和記錄型信號量當共享多種臨界資源時都容易引發死鎖問題。AND型信號量可以避免上述死鎖狀況的出現。
AND同步機制:要麼把進程在整個運行過程當中所請求的資源所有分配到進程(進程使用完成後一塊兒釋放),要麼一個也不分配。
描述:
wait():申請資源S1-Sn,當碰到Si不知足後,則執行else裏,將進程加入到Si關聯的等待隊列中 而進程指針移向wait()開始。wait()是原語,不可中斷。
wait(S1,S2,...,Sn) { if(S1>=1 and … and Sn>=1) { for(i:=1 to n) Si--; } else { place the process in the waiting queue associated with the first Si found with Si<1,and set the program count of this process to the beginning of Swait operation } }
signal():釋放資源S1-Sn,將S1-Sn關聯的等待隊列清空 並調入就緒隊列。
signal(S1,S2,...,Sn) { for(i:=1 to n) { Si++; Remove all the process waiting in the queue associated with Si into the ready queue. } }
不足:整型信號量、記錄型信號量侷限在只共享一種臨界資源。AND型信號量侷限在單種(一種)臨界資源爲1,若單種資源爲N(N>=1)時,AND型信號量的wait()和signal()就需操做N次,這是很低效的。
但不少狀況是一個進程可能申請多種臨界資源且某種臨界資源數目大於1的。
如上所述。當須要單種資源爲N(N>=1),AND型操做 wait()和signal()須要操做N次,低效。
除低效問題,還考慮情景:當某種資源數低於某一下限時變不在分配,所以每次都需檢測該資源數是否大於下限值。
從而衍生出了信號量集:
操做可描述以下,其中 S 爲信號量,d 爲需求值,而 t 爲下限值。
wait():申請S1-Sn, Si須要di個,而資源Si下限爲ti。當S1-Sn中,Si不知足下限值ti後,就不能分配了。進入else,進程加入到等待隊列,指針移向wait()開始位置。
wait(S1,t1,d1,…,Sn,tn,dn) { if (S1>=t1 and … and Sn>=tn){ for (i:=1 to n) Si:=Si-di; } else{ Place the executing process in the waiting queue of the first Si with Si<ti and set its program counter to the beginning of the wait Operation. } }
signal():釋放資源S1-Sn,Si釋放di個,S1-Sn關聯的等待隊列清空 並調入就緒隊列。
signal(S1,d1,…,Sn,dn) { for i:=1 to n do { Si:=Si+di; Remove all the process waiting in the queue associated with Si into the ready queue } }
「信號量集」的幾種特殊狀況:
(1) Swait(S,d,d)。此時在信號量集中只有一個信號量 S,每次申請 d 個資 源,當現有資源數少於d 時,不予分配。
(2) Swait(S,1,1)。此時的信號量集已蛻化爲通常的記錄型信號量(S>1 時)或互斥信號 量(S=1 時)。
(3) Swait(S,1,0)。這是一種很特殊且頗有用的信號量操做。當 S>=1 時,容許多個進程進入某特定區;當 S 變爲 0 後,將阻止任何進程進入特定區。換言之,它至關於一個可
控開關。
爲使多個進程能互斥地訪問某臨界資源,只須爲該資源設置一互斥信號量 mutex,並設其初始值爲 1,而後將各進程訪問該資源的臨界區 置於 wait(mutex)和 signal(mutex)操做之間便可。
Var mutex: semaphore:=1;
process 1: begin repeat wait(mutex); critical section signal(mutex); remainder seetion until false; end process 2: begin repeat wait(mutex); critical section signal(mutex); remainder section until false; end
在利用信號量機制實現進程互斥時應注意,wait(mutex)和 signal(mutex)必須成對地出現。 缺乏 wait(mutex)將會致使系統混亂,不能保證對臨界資源的互斥訪問;而缺乏 signal(mutex)將會使臨界資源永遠不被釋放,從而使因等待該資源而阻塞的進程不能被喚醒。
設有兩個併發執行的進程 P1 和 P2。P1 中有語句 S1;P2 中有語句 S2。
咱們但願在 S1 執行後再執行 S2。爲實現這種前趨關係: S1->S2。
咱們只須使進程 P1 和 P2 共享一個公用信號量 S,並賦予其初值爲 0,將 signal(S)操做放在語句 S1 後面;而在 S2 語句前面插入 wait(S)操做,即
在進程 P1 中
S1;
signal(S);
在進程 P2 中
wait(S);
S2;
因爲 S 被初始化爲 0,這樣,若 P2 先執行一定阻塞,只有在進程 P1 執行完 S1;signal(S);操做後使 S 增爲 1 時,P2 進程方能執行語句 S2 成功。
這是最基礎的實現,在此基礎上能夠實現更多進程間更復雜的先後驅關係,須要多個公共信號量協同操做。