進(jìn)程同步

** 本文摘自湯小丹主編《計(jì)算機(jī)操作系統(tǒng)》(第三版)2.3 進(jìn)程同步 **

在 OS 中引入進(jìn)程后,雖然提高了資源的利用率和系統(tǒng)的吞吐量,但由于進(jìn)程的異步性, 也會(huì)給系統(tǒng)造成混亂,尤其是在他們爭(zhēng)用臨界資源時(shí)。例如,當(dāng)多個(gè)進(jìn)程去爭(zhēng)用一臺(tái)打印 機(jī)時(shí),有可能使多個(gè)進(jìn)程的輸出結(jié)果交織在一起,難于區(qū)分;而當(dāng)多個(gè)進(jìn)程去爭(zhēng)用共享變 量、表格、鏈表時(shí),有可能致使數(shù)據(jù)處理出錯(cuò)。進(jìn)程同步的主要任務(wù)是對(duì)多個(gè)相關(guān)進(jìn)程在 執(zhí)行次序上進(jìn)行協(xié)調(diào),以使并發(fā)執(zhí)行的諸進(jìn)程之間能有效地共享資源和相互合作,從而使 程序的執(zhí)行具有可再現(xiàn)性。


涉及概念

進(jìn)程同步,臨界資源,臨界區(qū),信號(hào)量,進(jìn)程互斥,前趨關(guān)系,管程


進(jìn)程同步的基本概念

1. 兩種形式的制約關(guān)系

在多道程序環(huán)境下,當(dāng)程序并發(fā)執(zhí)行時(shí),由于資源共享和進(jìn)程合作,使同處于一個(gè)系 統(tǒng)中的諸進(jìn)程之間可能存在著以下兩種形式的制約關(guān)系。

  1. 間接相互制約關(guān)系。同處于一個(gè)系統(tǒng)中的進(jìn)程,通常都共享著某種系統(tǒng)資源,如共 享 CPU、共享 I/O 設(shè)備等。所謂間接相互制約即源于這種資源共享,例如,有兩個(gè)進(jìn)程 A 和 B,如果在 A 進(jìn)程提出打印請(qǐng)求時(shí),系統(tǒng)已將惟一的一臺(tái)打印機(jī)分配給了進(jìn)程 B,則此 時(shí)進(jìn)程 A 只能阻塞;一旦進(jìn)程 B 將打印機(jī)釋放,則 A 進(jìn)程才能由阻塞改為就緒狀態(tài)。
  2. 直接相互制約關(guān)系。這種制約主要源于進(jìn)程間的合作。例如,有一輸入進(jìn)程 A 通過(guò) 單緩沖向進(jìn)程 B 提供數(shù)據(jù)。當(dāng)該緩沖空時(shí),計(jì)算進(jìn)程因不能獲得所需數(shù)據(jù)而阻塞,而當(dāng)進(jìn) 程 A 把數(shù)據(jù)輸入緩沖區(qū)后,便將進(jìn)程 B 喚醒;反之,當(dāng)緩沖區(qū)已滿時(shí),進(jìn)程 A 因不能再向 緩沖區(qū)投放數(shù)據(jù)而阻塞,當(dāng)進(jìn)程 B 將緩沖區(qū)數(shù)據(jù)取走后便可喚醒 A。

2. 臨界資源

在第一章中我們?cè)?jīng)介紹過(guò),許多硬件資源如打印機(jī)、磁帶機(jī)等,都屬于臨界資源 (Critical Resouce),諸進(jìn)程間應(yīng)采取互斥方式,實(shí)現(xiàn)對(duì)這種資源的共享。下面我們將通過(guò)一 個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明這一過(guò)程。

生產(chǎn)者-消費(fèi)者(producer-consumer)問(wèn)題是一個(gè)著名的進(jìn)程同步問(wèn)題。它描述的是:有一 群生產(chǎn)者進(jìn)程在生產(chǎn)產(chǎn)品,并將這些產(chǎn)品提供給消費(fèi)者進(jìn)程去消費(fèi)。為使生產(chǎn)者進(jìn)程與消 費(fèi)者進(jìn)程能并發(fā)執(zhí)行,在兩者之間設(shè)置了一個(gè)具有 n 個(gè)緩沖區(qū)的緩沖池,生產(chǎn)者進(jìn)程將它 所生產(chǎn)的產(chǎn)品放入一個(gè)緩沖區(qū)中;消費(fèi)者進(jìn)程可從一個(gè)緩沖區(qū)中取走產(chǎn)品去消費(fèi)。盡管所 有的生產(chǎn)者進(jìn)程和消費(fèi)者進(jìn)程都是以異步方式運(yùn)行的,但它們之間必須保持同步,即不允 許消費(fèi)者進(jìn)程到一個(gè)空緩沖區(qū)去取產(chǎn)品,也不允許生產(chǎn)者進(jìn)程向一個(gè)已裝滿產(chǎn)品且尚未被 取走的緩沖區(qū)中投放產(chǎn)品。

我們可利用一個(gè)數(shù)組來(lái)表示上述的具有 n 個(gè)(0,1,...,n-1)緩沖區(qū)的緩沖池。用輸入 指針 in 來(lái)指示下一個(gè)可投放產(chǎn)品的緩沖區(qū),每當(dāng)生產(chǎn)者進(jìn)程生產(chǎn)并投放一個(gè)產(chǎn)品后,輸入 指針加 1;用一個(gè)輸出指針 out 來(lái)指示下一個(gè)可從中獲取產(chǎn)品的緩沖區(qū),每當(dāng)消費(fèi)者進(jìn)程取 走一個(gè)產(chǎn)品后,輸出指針加 1。由于這里的緩沖池是組織成循環(huán)緩沖的,故應(yīng)把輸入指針加 1 表示成 in:= (in+1)mod n; 輸出指針加 1 表示成 out:= (out+1) mod n。當(dāng) (in+1) mod n=out 時(shí)表示緩沖池滿;而 in=out 則表示緩沖池空。此外,還引入了一個(gè)整型變量 counter,其初 始值為 0。每當(dāng)生產(chǎn)者進(jìn)程向緩沖池中投放一個(gè)產(chǎn)品后,使 counter 加 1;反之,每當(dāng)消費(fèi) 者進(jìn)程從中取走一個(gè)產(chǎn)品時(shí),使 counter 減 1。生產(chǎn)者和消費(fèi)者兩進(jìn)程共享下面的變量:

Var n,integer;
type item=...;
var buffer: array[0,1,...,n-1] of item; in,out: 0,1,...,n-1;
counter: 0,1,...,n;

指針 in 和 out 初始化為 1。在生產(chǎn)者和消費(fèi)者進(jìn)程的描述中,noop 是一條空操作指令, while condition do no-op 語(yǔ)句表示重復(fù)的測(cè)試條件(condication),重復(fù)測(cè)試應(yīng)進(jìn)行到該條件變 為 false(假),即到該條件不成立時(shí)為止。在生產(chǎn)者進(jìn)程中使用一局部變量 nextp,用于暫時(shí) 存放每次剛生產(chǎn)出來(lái)的產(chǎn)品;而在消費(fèi)者進(jìn)程中,則使用一個(gè)局部變量 nextc,用于存放每 次要消費(fèi)的產(chǎn)品。

producer: 
    repeat
        ...
        produce an item in nextp; 
        ...
        while counter=n do no-op; 
        buffer[in]:=nextp; 
        in:=in+1 mod n; 
        counter:=counter+1;
    until false;

consumer: 
    repeat
        while counter=0 do no-op; 
        nextc:=buffer[out]; 
        out:=(out+1) mod n; 
        counter:=counter-1; 
        consumer the item in nextc;
    until false;    

雖然上面的生產(chǎn)者程序和消費(fèi)者程序在分別看時(shí)都是正確的,而且兩者在順序執(zhí)行時(shí)
其結(jié)果也會(huì)是正確的,但若并發(fā)執(zhí)行時(shí)就會(huì)出現(xiàn)差錯(cuò),問(wèn)題就在于這兩個(gè)進(jìn)程共享變量 counter。生產(chǎn)者對(duì)它做加 1 操作,消費(fèi)者對(duì)它做減 1 操作,這兩個(gè)操作在用機(jī)器語(yǔ)言實(shí)現(xiàn) 時(shí), 常可用下面的形式描述:

register1:=counter; register2:=counter; 
register1:=register1+1; register2:=register2-1; 
counter:=register1; counter:=register2;

假設(shè) counter 的當(dāng)前值是 5。如果生產(chǎn)者進(jìn)程先執(zhí)行左列的三條機(jī)器語(yǔ)言語(yǔ)句,然后消 費(fèi)者進(jìn)程再執(zhí)行右列的三條語(yǔ)句,則最后共享變量 counter 的值仍為 5; 反之,如果讓消費(fèi) 者進(jìn)程先執(zhí)行右列的三條語(yǔ)句,然后再讓生產(chǎn)者進(jìn)程執(zhí)行左列的三條語(yǔ)句,則 counter 值也 還是 5,但是,如果按下述順序執(zhí)行:

register1:=counter;         (register1=5) 
register1:=register1+1;     (register1=6)
register2:=counter;         (register2=5)
register2:=register2-1;     (register2=4)
counter:=register1;         (counter=6) 
counter:=register2;         (counter=4)
    

正確的 counter 值應(yīng)當(dāng)是 5,但現(xiàn)在是 4。讀者可以自己試試,倘若再將兩段程序中各語(yǔ)句 交叉執(zhí)行的順序改變,將可看到又可能得到 counter=6 的答案,這表明程序的執(zhí)行已經(jīng)失去 了再現(xiàn)性。為了預(yù)防產(chǎn)生這種錯(cuò)誤,解決此問(wèn)題的關(guān)鍵是應(yīng)把變量 counter 作為臨界資源處 理,亦即,令生產(chǎn)者進(jìn)程和消費(fèi)者進(jìn)程互斥地訪問(wèn)變量 counter。

3. 臨界區(qū)

由前所述可知,不論是硬件臨界資源,還是軟件臨界資源,多個(gè)進(jìn)程必須互斥地對(duì)它進(jìn)行訪問(wèn)。人們把在每個(gè)進(jìn)程中訪問(wèn)臨界資源的那段代碼稱為臨界區(qū)(critical section)。顯然, 若能保證諸進(jìn)程互斥地進(jìn)入自己的臨界區(qū),便可實(shí)現(xiàn)諸進(jìn)程對(duì)臨界資源的互斥訪問(wèn)。為此, 每個(gè)進(jìn)程在進(jìn)入臨界區(qū)之前,應(yīng)先對(duì)欲訪問(wèn)的臨界資源進(jìn)行檢查,看它是否正被訪問(wèn)。如 果此刻該臨界資源未被訪問(wèn),進(jìn)程便可進(jìn)入臨界區(qū)對(duì)該資源進(jìn)行訪問(wèn),并設(shè)置它正被訪問(wèn) 的標(biāo)志;如果此刻該臨界資源正被某進(jìn)程訪問(wèn),則本進(jìn)程不能進(jìn)入臨界區(qū)。因此,必須在 臨界區(qū)前面增加一段用于進(jìn)行上述檢查的代碼,把這段代碼稱為進(jìn)入?yún)^(qū)(entry section)。相應(yīng) 地,在臨界區(qū)后面也要加上一段稱為退出區(qū)(exit section)的代碼,用于將臨界區(qū)正被訪問(wèn)的 標(biāo)志恢復(fù)為未被訪問(wèn)的標(biāo)志。進(jìn)程中除上述進(jìn)入?yún)^(qū)、臨界區(qū)及退出區(qū)之外的其它部分的代 碼,在這里都稱為剩余區(qū)。這樣,可把一個(gè)訪問(wèn)臨界資源的循環(huán)進(jìn)程描述如下:

    repeat
        |entry section|
        critical section;
        |exit section|
        remainder section;
    until false;

4. 同步機(jī)制應(yīng)遵循的規(guī)則

為實(shí)現(xiàn)進(jìn)程互斥地進(jìn)入自已的臨界區(qū),可用軟件方法,更多的是在系統(tǒng)中設(shè)置專門的 同步機(jī)構(gòu)來(lái)協(xié)調(diào)各進(jìn)程間的運(yùn)行。所有同步機(jī)制都應(yīng)遵循下述四條準(zhǔn)則:

  1. 空閑讓進(jìn)。當(dāng)無(wú)進(jìn)程處于臨界區(qū)時(shí),表明臨界資源處于空閑狀態(tài),應(yīng)允許一個(gè)請(qǐng)求 進(jìn)入臨界區(qū)的進(jìn)程立即進(jìn)入自己的臨界區(qū),以有效地利用臨界資源。
  2. 忙則等待。當(dāng)已有進(jìn)程進(jìn)入臨界區(qū)時(shí),表明臨界資源正在被訪問(wèn),因而其它試圖進(jìn) 入臨界區(qū)的進(jìn)程必須等待,以保證對(duì)臨界資源的互斥訪問(wèn)。
  3. 有限等待。對(duì)要求訪問(wèn)臨界資源的進(jìn)程,應(yīng)保證在有限時(shí)間內(nèi)能進(jìn)入自己的臨界區(qū), 以免陷入“死等”狀態(tài)。
  4. 讓權(quán)等待。當(dāng)進(jìn)程不能進(jìn)入自己的臨界區(qū)時(shí),應(yīng)立即釋放處理機(jī),以免進(jìn)程陷入“忙 等”狀態(tài)。

信號(hào)量機(jī)制

1965 年,荷蘭學(xué)者 Dijkstra 提出的信號(hào)量(Semaphores)機(jī)制是一種卓有成效的進(jìn)程同步 工具。在長(zhǎng)期且廣泛的應(yīng)用中,信號(hào)量機(jī)制又得到了很大的發(fā)展,它從整型信號(hào)量經(jīng)記錄 型信號(hào)量,進(jìn)而發(fā)展為“信號(hào)量集”機(jī)制。現(xiàn)在,信號(hào)量機(jī)制已被廣泛地應(yīng)用于單處理機(jī) 和多處理機(jī)系統(tǒng)以及計(jì)算機(jī)網(wǎng)絡(luò)中。

1. 整型信號(hào)量

最初由 Dijkstra 把整型信號(hào)量定義為一個(gè)用于表示資源數(shù)目的整型量 S,它與一般整型 量不同,除初始化外,僅能通過(guò)兩個(gè)標(biāo)準(zhǔn)的原子操作(Atomic Operation) wait(S)和signal(S) 來(lái)訪問(wèn)。很長(zhǎng)時(shí)間以來(lái),這兩個(gè)操作一直被分別稱為 P、V 操作。Wait(S)和 signal(S)操作可 描述為:

wait(S):    while S<=0 do no-op;
                  S:=S-1;                 
signal(S):        S:=S+1;

wait(S)和 signal(S)是兩個(gè)原子操作,因此,它們?cè)趫?zhí)行時(shí)是不可中斷的。亦即,當(dāng)一個(gè) 進(jìn)程在修改某信號(hào)量時(shí),沒(méi)有其他進(jìn)程可同時(shí)對(duì)該信號(hào)量進(jìn)行修改。此外,在 wait 操作中, 對(duì) S 值的測(cè)試和做 S:=S-1 操作時(shí)都不可中斷。

2. 記錄型信號(hào)量

在整型信號(hào)量機(jī)制中的 wait 操作,只要是信號(hào)量 S≤0,就會(huì)不斷地測(cè)試。因此,該機(jī) 制并未遵循“讓權(quán)等待”的準(zhǔn)則,而是使進(jìn)程處于“忙等”的狀態(tài)。記錄型信號(hào)量機(jī)制則 是一種不存在“忙等”現(xiàn)象的進(jìn)程同步機(jī)制。但在采取了“讓權(quán)等待”的策略后,又會(huì)出 現(xiàn)多個(gè)進(jìn)程等待訪問(wèn)同一臨界資源的情況。為此,在信號(hào)量機(jī)制中,除了需要一個(gè)用于代 表資源數(shù)目的整型變量 value 外,還應(yīng)增加一個(gè)進(jìn)程鏈表指針 L,用于鏈接上述的所有等待 進(jìn)程。記錄型信號(hào)量是由于它采用了記錄型的數(shù)據(jù)結(jié)構(gòu)而得名的。它所包含的上述兩個(gè)數(shù) 據(jù)項(xiàng)可描述為:

type semaphore=record
               value: integer;
               L: list of process;
               end

相應(yīng)地,wait(S)和 signal(S)操作可描述為:

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

在記錄型信號(hào)量機(jī)制中,S.value 的初值表示系統(tǒng)中某類資源的數(shù)目,因而又稱為資源 信號(hào)量。對(duì)它的每次 wait 操作,意味著進(jìn)程請(qǐng)求一個(gè)單位的該類資源,使系統(tǒng)中可供分配 的該類資源數(shù)減少一個(gè),因此描述為 S.value:=S.value-1;當(dāng) S.value<0 時(shí),表示該類資源已 分配完畢,因此進(jìn)程應(yīng)調(diào)用 block 原語(yǔ),進(jìn)行自我阻塞,放棄處理機(jī),并插入到信號(hào)量鏈表 S.L 中。可見(jiàn),該機(jī)制遵循了“讓權(quán)等待”準(zhǔn)則。此時(shí) S.value 的絕對(duì)值表示在該信號(hào)量鏈 表中已阻塞進(jìn)程的數(shù)目。對(duì)信號(hào)量的每次 signal 操作,表示執(zhí)行進(jìn)程釋放一個(gè)單位資源,使 系統(tǒng)中可供分配的該類資源數(shù)增加一個(gè),故 S.value:=S.value+1 操作表示資源數(shù)目加 1。若 加 1 后仍是 S.value≤0,則表示在該信號(hào)量鏈表中,仍有等待該資源的進(jìn)程被阻塞,故還應(yīng) 調(diào)用 wakeup 原語(yǔ),將 S.L 鏈表中的第一個(gè)等待進(jìn)程喚醒。如果 S.value 的初值為 1,表示只允許一個(gè)進(jìn)程訪問(wèn)臨界資源,此時(shí)的信號(hào)量轉(zhuǎn)化為互斥信號(hào)量,用于進(jìn)程互斥。

3. AND 型信號(hào)量

上述的進(jìn)程互斥問(wèn)題,是針對(duì)各進(jìn)程之間只共享一個(gè)臨界資源而言的。在有些應(yīng)用場(chǎng) 合,是一個(gè)進(jìn)程需要先獲得兩個(gè)或更多的共享資源后方能執(zhí)行其任務(wù)。假定現(xiàn)有兩個(gè)進(jìn)程 A 和 B,他們都要求訪問(wèn)共享數(shù)據(jù) D 和 E。當(dāng)然,共享數(shù)據(jù)都應(yīng)作為臨界資源。為此,可為 這兩個(gè)數(shù)據(jù)分別設(shè)置用于互斥的信號(hào)量 Dmutex 和 Emutex,并令它們的初值都是 1。相應(yīng)地, 在兩個(gè)進(jìn)程中都要包含兩個(gè)對(duì) Dmutex 和 Emutex 的操作,即

process A: process B:
wait(Dmutex); wait(Emutex);
wait(Emutex); wait(Dmutex);

若進(jìn)程 A 和 B 按下述次序交替執(zhí)行 wait 操作

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 阻塞

最后,進(jìn)程 A 和 B 處于僵持狀態(tài)。在無(wú)外力作用下,兩者都將無(wú)法從僵持狀態(tài)中解脫 出來(lái)。我們稱此時(shí)的進(jìn)程 A 和 B 已進(jìn)入死鎖狀態(tài)。顯然,當(dāng)進(jìn)程同時(shí)要求的共享資源愈多 時(shí),發(fā)生進(jìn)程死鎖的可能性也就愈大。

AND 同步機(jī)制的基本思想是:將進(jìn)程在整個(gè)運(yùn)行過(guò)程中需要的所有資源,一次性全部 地分配給進(jìn)程,待進(jìn)程使用完后再一起釋放。只要尚有一個(gè)資源未能分配給進(jìn)程,其它所 有可能為之分配的資源也不分配給它。亦即,對(duì)若干個(gè)臨界資源的分配,采取原子操作方 式:要么把它所請(qǐng)求的資源全部分配到進(jìn)程,要么一個(gè)也不分配。由死鎖理論可知,這樣 就可避免上述死鎖情況的發(fā)生。為此,在 wait 操作中,增加了一個(gè)“AND”條件,故稱為 AND 同步,或稱為同時(shí) wait 操作,即 Swait(Simultaneous wait)定義如下:

Swait(S1,S2,...,Sn)
    if Si>=1 and ... and Sn>=1 then
        for i:=1 to n do 
        Si:=Si-1; 
        endfor
    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
    endif

Ssignal(S1,S2,...,Sn) 
for i:=1 to n do
    Si:=Si+1;
Remove all the process waiting in the queue associated with Si into the ready queue.
endfor;

4. 信號(hào)量集

在記錄型信號(hào)量機(jī)制中,wait(S)或 signal(S)操作僅能對(duì)信號(hào)量施以加 1 或減 1 操作,意 味著每次只能獲得或釋放一個(gè)單位的臨界資源。而當(dāng)一次需要 N 個(gè)某類臨界資源時(shí),便要 進(jìn)行 N 次 wait(S)操作,顯然這是低效的。此外,在有些情況下,當(dāng)資源數(shù)量低于某一下限 值時(shí),便不予以分配。因而,在每次分配之前,都必須測(cè)試該資源的數(shù)量,看其是否大于 其下限值。基于上述兩點(diǎn),可以對(duì) AND 信號(hào)量機(jī)制加以擴(kuò)充,形成一般化的“信號(hào)量集” 機(jī)制。Swait 操作可描述如下,其中 S 為信號(hào)量,d 為需求值,而 t 為下限值。

Swait(S1,t1,d1,...,Sn,tn,dn) 
    if Si>=t1 and ... and Sn>=tn then
        for i:=1 to n do 
            Si:=Si-di;
    endfor 
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 Swait Operation.
endif


Ssignal(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 
endfor;

下面我們討論一般“信號(hào)量集”的幾種特殊情況:

  1. Swait(S,d,d)。此時(shí)在信號(hào)量集中只有一個(gè)信號(hào)量 S,但允許它每次申請(qǐng) d 個(gè)資 源,當(dāng)現(xiàn)有資源數(shù)少于 d 時(shí),不予分配。
  2. Swait(S,1,1)。此時(shí)的信號(hào)量集已蛻化為一般的記錄型信號(hào)量(S>1時(shí))或互斥信號(hào) 量(S=1 時(shí))。
  3. Swait(S,1,0)。這是一種很特殊且很有用的信號(hào)量操作。當(dāng)S≥1時(shí),允許多個(gè)進(jìn) 程進(jìn)入某特定區(qū);當(dāng) S 變?yōu)?0 后,將阻止任何進(jìn)程進(jìn)入特定區(qū)。換言之,它相當(dāng)于一個(gè)可 控開關(guān)。

信號(hào)量的應(yīng)用

1. 利用信號(hào)量實(shí)現(xiàn)進(jìn)程互斥

為使多個(gè)進(jìn)程能互斥地訪問(wèn)某臨界資源,只須為該資源設(shè)置一互斥信號(hào)量 mutex,并設(shè) 其初始值為 1,然后將各進(jìn)程訪問(wèn)該資源的臨界區(qū) CS 置于 wait(mutex)和 signal(mutex)操作 之間即可。這樣,每個(gè)欲訪問(wèn)該臨界資源的進(jìn)程在進(jìn)入臨界區(qū)之前,都要先對(duì) mutex 執(zhí)行 wait 操作,若該資源此刻未被訪問(wèn),本次 wait 操作必然成功,進(jìn)程便可進(jìn)入自己的臨界區(qū), 這時(shí)若再有其他進(jìn)程也欲進(jìn)入自己的臨界區(qū),此時(shí)由于對(duì) mutex 執(zhí)行 wait 操作定會(huì)失敗,因而該進(jìn)程阻塞,從而保證了該臨界資源能被互斥地訪問(wèn)。當(dāng)訪問(wèn)臨界資源的進(jìn)程退出臨 界區(qū)后,又應(yīng)對(duì) mutex 執(zhí)行 signal 操作,以便釋放該臨界資源。利用信號(hào)量實(shí)現(xiàn)進(jìn)程互斥的 進(jìn)程可描述如下:

Var mutex: semaphore:=1; 
    begin
    parbegin
        process 1: begin
                        repeat 
                            wait(mutex); 
                            critical section 
                            signal(mutex); 
                            remainder seetion
                        until false; 
                    end
                    
        process 2: begin 
                        repeat
                            wait(mutex); 
                            ritical section 
                            signal(mutex); 
                            remainder section
                        until false; 
                    end
    parend

在利用信號(hào)量機(jī)制實(shí)現(xiàn)進(jìn)程互斥時(shí)應(yīng)注意,wait(mutex)和 signal(mutex)必須成對(duì)地出現(xiàn)。缺少 wait(mutex)將會(huì)導(dǎo)致系統(tǒng)混亂,不能保證對(duì)臨界資源的互斥訪問(wèn);而缺少 signal(mutex) 將會(huì)使臨界資源永遠(yuǎn)不被釋放,從而使因等待該資源而阻塞的進(jìn)程不能被喚醒。

2. 利用信號(hào)量實(shí)現(xiàn)前趨關(guān)系

還可利用信號(hào)量來(lái)描述程序或語(yǔ)句之間的前趨關(guān)系。設(shè)有兩個(gè)并發(fā)執(zhí)行的進(jìn)程 P1 和 P2。 P1 中有語(yǔ)句 S1;P2 中有語(yǔ)句 S2。我們希望在 S1 執(zhí)行后再執(zhí)行 S2。為實(shí)現(xiàn)這種前趨關(guān)系, 我們只須使進(jìn)程 P1 和 P2 共享一個(gè)公用信號(hào)量 S,并賦予其初值為 0,將 signal(S)操作放在 語(yǔ)句 S1 后面;而在 S2 語(yǔ)句前面插入 wait(S)操作,即

  • 在進(jìn)程 P1 中,用 S1;signal(S);

  • 在進(jìn)程 P2 中,用 wait(S);S2;

  • 由于 S 被初始化為 0,這樣,若 P2 先執(zhí)行必定阻塞,只有在進(jìn)程 P1 執(zhí)行完 S1;signal(S);操作后使 S 增為 1 時(shí), P2 進(jìn)程方能執(zhí)行語(yǔ)句 S2 成功。同樣,我們可以利用信號(hào) 量,按照語(yǔ)句間的前趨關(guān)系(見(jiàn)圖),寫出一個(gè)更為復(fù) 雜的可并發(fā)執(zhí)行的程序。


    前趨關(guān)系圖

圖示是一個(gè)前趨圖,其中 S1,S2,S3,...,S6 是最簡(jiǎn)單的程序段(只有一條語(yǔ)句)。 為使各程序段能正確執(zhí)行,應(yīng)設(shè)置若干個(gè)初始值為“0”的信號(hào)量。如為保證 S1→S2,S1→S3 的前趨關(guān)系,應(yīng)分別設(shè)置信號(hào)量 a 和 b,同樣,為了保證 S2→S4,S2→S5,S3→S6,S4→S6 和 S5→S6,應(yīng)設(shè)置信號(hào)量 c,d,e,f,g。

Var a,b,c,d,e,f,g:semaphore: =0,0,0,0,0,0,0; 
    begin
        parbegin
            begin S1; signal(a); signal(b); end;
            begin wait(a); S2; signal(c); 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); wait(g); S6; end;
        parend 
    end 

管程機(jī)制

雖然信號(hào)量機(jī)制是一種既方便、又有效的進(jìn)程同步機(jī)制,但每個(gè)要訪問(wèn)臨界資源的進(jìn) 程都必須自備同步操作 wait(S)和 signal(S)。這就使大量的同步操作分散在各個(gè)進(jìn)程中。這 不僅給系統(tǒng)的管理帶來(lái)了麻煩,而且還會(huì)因同步操作的使用不當(dāng)而導(dǎo)致系統(tǒng)死鎖。這樣, 在解決上述問(wèn)題的過(guò)程中,便產(chǎn)生了一種新的進(jìn)程同步工具——管程(Monitors)。

1. 管程的定義

系統(tǒng)中的各種硬件資源和軟件資源,均可用數(shù)據(jù)結(jié)構(gòu)抽象地描述其資源特性,即用少 量信息和對(duì)該資源所執(zhí)行的操作來(lái)表征該資源,而忽略了它們的內(nèi)部結(jié)構(gòu)和實(shí)現(xiàn)細(xì)節(jié)。例 如,對(duì)一臺(tái)電傳機(jī),可用與分配該資源有關(guān)的狀態(tài)信息(busy 或 free)和對(duì)它執(zhí)行請(qǐng)求與釋放 的操作,以及等待該資源的進(jìn)程隊(duì)列來(lái)描述。又如,一個(gè) FIFO 隊(duì)列,可用其隊(duì)長(zhǎng)、隊(duì)首和 隊(duì)尾以及在該隊(duì)列上執(zhí)行的一組操作來(lái)描述。

利用共享數(shù)據(jù)結(jié)構(gòu)抽象地表示系統(tǒng)中的共享資源,而把對(duì)該共享數(shù)據(jù)結(jié)構(gòu)實(shí)施的操作 定義為一組過(guò)程,如資源的請(qǐng)求和釋放過(guò)程 request 和 release。進(jìn)程對(duì)共享資源的申請(qǐng)、釋 放和其它操作,都是通過(guò)這組過(guò)程對(duì)共享數(shù)據(jù)結(jié)構(gòu)的操作來(lái)實(shí)現(xiàn)的,這組過(guò)程還可以根據(jù) 資源的情況,或接受或阻塞進(jìn)程的訪問(wèn),確保每次僅有一個(gè)進(jìn)程使用共享資源,這樣就可 以統(tǒng)一管理對(duì)共享資源的所有訪問(wèn),實(shí)現(xiàn)進(jìn)程互斥。

代表共享資源的數(shù)據(jù)結(jié)構(gòu),以及由對(duì)該共享數(shù)據(jù)結(jié)構(gòu)實(shí)施操作的一組過(guò)程所組成的資源 管理程序,共同構(gòu)成了一個(gè)操作系統(tǒng)的資源管理模塊,我們稱之為管程。管程被請(qǐng)求和釋放 資源的進(jìn)程所調(diào)用。Hansan 為管程所下的定義是:“一個(gè)管程定義了一個(gè)數(shù)據(jù)結(jié)構(gòu)和能為并 發(fā)進(jìn)程所執(zhí)行(在該數(shù)據(jù)結(jié)構(gòu)上)的一組操作,這組操作能同步進(jìn)程和改變管程中的數(shù)據(jù)”。

由上述的定義可知,管程由四部分組成:1 管程的名稱;2 局部于管程內(nèi)部的共享 數(shù)據(jù)結(jié)構(gòu)說(shuō)明;3 對(duì)該數(shù)據(jù)結(jié)構(gòu)進(jìn)行操作的一組過(guò)程;4 對(duì)局部于管程內(nèi)部的共享數(shù)據(jù) 設(shè)置初始值的語(yǔ)句。圖是一個(gè)管程的示意圖。

管程示意圖

管程的語(yǔ)法描述如下:

    type monitor_name = MONITOR;
<共享變量說(shuō)明>;
define <(能被其他模塊引用的)過(guò)程名列表>; 
use <(要調(diào)用的本模塊外定義的)過(guò)程名列表>; 
procedure <過(guò)程名>(<形式參數(shù)表>);
    begin
        M
    end;
        M
function <函數(shù)名>(<形式參數(shù)表>):值類型; 
    begin
        M
    end; 
        M
begin 
    <管程的局部數(shù)據(jù)初始化語(yǔ)句序列>;
end

需要指出的是,局部于管程內(nèi)部的數(shù)據(jù)結(jié)構(gòu),僅能被局部于管程內(nèi)部的過(guò)程所訪問(wèn), 任何管程外的過(guò)程都不能訪問(wèn)它;反之,局部于管程內(nèi)部的過(guò)程也僅能訪問(wèn)管程內(nèi)的數(shù)據(jù) 結(jié)構(gòu)。由此可見(jiàn),管程相當(dāng)于圍墻,它把共享變量和對(duì)它進(jìn)行操作的若干過(guò)程圍了起來(lái), 所有進(jìn)程要訪問(wèn)臨界資源時(shí),都必須經(jīng)過(guò)管程(相當(dāng)于通過(guò)圍墻的門)才能進(jìn)入,而管程每次 只準(zhǔn)許一個(gè)進(jìn)程進(jìn)入管程,從而實(shí)現(xiàn)了進(jìn)程互斥。

管程是一種程序設(shè)計(jì)語(yǔ)言結(jié)構(gòu)成分,它和信號(hào)量有同等的表達(dá)能力,從語(yǔ)言的角度看, 管程主要有以下特性:

  1. 模塊化。管程是一個(gè)基本程序單位,可以單獨(dú)編譯。
  2. 抽象數(shù)據(jù)類型。管程中不僅有數(shù)據(jù),而且有對(duì)數(shù)據(jù)的操作。
  3. 信息掩蔽。管程中的數(shù)據(jù)結(jié)構(gòu)只能被管程中的過(guò)程訪問(wèn),這些過(guò)程也是在管程內(nèi)部 定義的,供管程外的進(jìn)程調(diào)用,而管程中的數(shù)據(jù)結(jié)構(gòu)以及過(guò)程(函數(shù))的具體實(shí)現(xiàn)外部不可見(jiàn)。

管程和進(jìn)程不同,主要體現(xiàn)在以下幾個(gè)方面:

  1. 雖然二者都定義了數(shù)據(jù)結(jié)構(gòu),但進(jìn)程定義的是私有數(shù)據(jù)結(jié)構(gòu) PCB,管程定義的是公 共數(shù)據(jù)結(jié)構(gòu),如消息隊(duì)列等;
  2. 二者都存在對(duì)各自數(shù)據(jù)結(jié)構(gòu)上的操作,但進(jìn)程是由順序程序執(zhí)行有關(guān)的操作,而管 程主要是進(jìn)行同步操作和初始化操作;
  3. 設(shè)置進(jìn)程的目的在于實(shí)現(xiàn)系統(tǒng)的并發(fā)性,而管程的設(shè)置則是解決共享資源的互斥使 用問(wèn)題;
  4. 進(jìn)程通過(guò)調(diào)用管程中的過(guò)程對(duì)共享數(shù)據(jù)結(jié)構(gòu)實(shí)行操作,該過(guò)程就如通常的子程序一 樣被調(diào)用,因而管程為被動(dòng)工作方式,進(jìn)程則為主動(dòng)工作方式;
  5. 進(jìn)程之間能并發(fā)執(zhí)行,而管程則不能與其調(diào)用者并發(fā);
  6. 進(jìn)程具有動(dòng)態(tài)性,由“創(chuàng)建”而誕生,由“撤銷”而消亡,而管程則是操作系統(tǒng)中 的一個(gè)資源管理模塊,供進(jìn)程調(diào)用。

2. 條件變量

在利用管程實(shí)現(xiàn)進(jìn)程同步時(shí),必須設(shè)置同步工具,如兩個(gè)同步操作原語(yǔ) wait 和 signal。 當(dāng)某進(jìn)程通過(guò)管程請(qǐng)求獲得臨界資源而未能滿足時(shí),管程便調(diào)用 wait 原語(yǔ)使該進(jìn)程等待, 并將其排在等待隊(duì)列上,如管程示意圖所示。僅當(dāng)另一進(jìn)程訪問(wèn)完成并釋放該資源之后,管程 才又調(diào)用 signal 原語(yǔ),喚醒等待隊(duì)列中的隊(duì)首進(jìn)程。

但是僅僅有上述的同步工具是不夠的。考慮一種情況:當(dāng)一個(gè)進(jìn)程調(diào)用了管程,在管 程中時(shí)被阻塞或掛起,直到阻塞或掛起的原因解除,而在此期間,如果該進(jìn)程不釋放管程, 則其它進(jìn)程無(wú)法進(jìn)入管程,被迫長(zhǎng)時(shí)間地等待。為了解決這個(gè)問(wèn)題,引入了條件變量 condition。通常,一個(gè)進(jìn)程被阻塞或掛起的條件(原因)可有多個(gè),因此在管程中設(shè)置了多個(gè) 條件變量,對(duì)這些條件變量的訪問(wèn),只能在管程中進(jìn)行。

管程中對(duì)每個(gè)條件變量都須予以說(shuō)明,其形式為:Var x,y:condition。對(duì)條件變量的 操作僅僅是 wait 和 signal,因此條件變量也是一種抽象數(shù)據(jù)類型,每個(gè)條件變量保存了一個(gè) 鏈表,用于記錄因該條件變量而阻塞的所有進(jìn)程,同時(shí)提供的兩個(gè)操作即可表示為 x.wait 和 x.signal,其含義為:

  1. x.wait:正在調(diào)用管程的進(jìn)程因x條件需要被阻塞或掛起,則調(diào)用x.wait將自己插 入到 x 條件的等待隊(duì)列上,并釋放管程,直到 x 條件變化。此時(shí)其它進(jìn)程可以使用該管程。
  2. x.signal:正在調(diào)用管程的進(jìn)程發(fā)現(xiàn)x條件發(fā)生了變化,則調(diào)用x.signal,重新啟動(dòng) 一個(gè)因 x 條件而阻塞或掛起的進(jìn)程。如果存在多個(gè)這樣的進(jìn)程,則選擇其中的一個(gè),如果 沒(méi)有,則繼續(xù)執(zhí)行原進(jìn)程,而不產(chǎn)生任何結(jié)果。這與信號(hào)量機(jī)制中的 signal 操作不同,因?yàn)?后者總是要執(zhí)行 s:=s+1 操作,因而總會(huì)改變信號(hào)量的狀態(tài)。

如果有進(jìn)程 Q 因 x 條件處于阻塞狀態(tài),當(dāng)正在調(diào)用管程的進(jìn)程 P 執(zhí)行了 x.signal 操作后, 進(jìn)程 Q 被重新啟動(dòng),此時(shí)兩個(gè)進(jìn)程 P 和 Q,如何確定哪個(gè)執(zhí)行,哪個(gè)等待,可采用下述兩 種方式之一進(jìn)行處理:

  1. P等待,直至Q離開管程或等待另一條件。
  2. Q等待,直至P離開管程或等待另一條件。

采用哪種處理方式,當(dāng)然是各執(zhí)一詞。Hoare 采用了第一種處理方式,而 Hansan 選擇了兩者的折衷,他規(guī)定管程中的過(guò)程所執(zhí)行的 signal 操作是過(guò)程體的最后一個(gè)操作,于是, 進(jìn)程 P 執(zhí)行 signal 操作后立即退出管程,因而進(jìn)程 Q 馬上被恢復(fù)執(zhí)行。

http://gurglessh.github.io/2016/04/12/進(jìn)程同步/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 又來(lái)到了一個(gè)老生常談的問(wèn)題,應(yīng)用層軟件開發(fā)的程序員要不要了解和深入學(xué)習(xí)操作系統(tǒng)呢? 今天就這個(gè)問(wèn)題開始,來(lái)談?wù)劜?..
    tangsl閱讀 4,165評(píng)論 0 23
  • 進(jìn)程同步概念 并發(fā)進(jìn)程在執(zhí)行次序上的協(xié)調(diào),以達(dá)到有效的資源共享和相互合作,使程序執(zhí)行有可再現(xiàn)性。 進(jìn)程間的關(guān)系 資...
    nino天閱讀 554評(píng)論 0 1
  • 第二十八章:鬼節(jié)&廟會(huì)的準(zhǔn)備工作(終) 日?日/7:00am: “哈欠~大家昨晚睡得還好嗎?”奈伊揉了揉眼睛,...
    森系的盒子閱讀 274評(píng)論 0 1
  • 今天早上發(fā)了一個(gè)朋友圈,立下目標(biāo)在6月5號(hào)之前要做到11點(diǎn)睡覺(jué),4點(diǎn)30起床,其實(shí)這個(gè)目標(biāo)在4月份的時(shí)候,就定下來(lái)...
    楊穎__指數(shù)成長(zhǎng)閱讀 895評(píng)論 4 3
  • 【This is going to be personal. For me, not for you.】 這標(biāo)題我...
    無(wú)間世閱讀 224評(píng)論 0 0