計算機作業系統(湯小丹第四版)程式同步

qwer1030274531發表於2020-11-10

程式同步

1.程式同步的基本概念

程式同步的主要任務是對多個相關程式在執行次序上進行協調,
使併發執行的諸程式之間能夠按照一定的規則(或時序)共享系
統資源,並能很好的相互合作,從而使程式的執行具有可再現性。123
兩種制約關係

在多道程式環境下,對於處於一個系統中的多個程式,由於他們共享資源,或為完成某一任務而相互合作,他們之間可能存在著以下兩種形式的制約關係:
1) 間接相互制約:多個程式在併發執行時,由於共享系統資源,如CPU,I/O裝置等,致使這些併發執行的程式之間形成相互制約的關係。為了保證這些程式的能有序執行,對於系統中的這類資源,必須由系統統一分配,即使用者在使用之前,應先提出申請,而不允許使用者程式直接使用。 這種制約關係源於對該類資源的共享。
2) 直接相互制約關係:為了完成某任務而建立的兩個或者多個程式。這些程式將為完成同一項任務而相互合作。例如生產者——消費者問題就存在著相互制約關係 程式間的直接制約關係源於他們之間的相互合作。

在多道程式環境下,由於存在著上述兩類相互制約關係,程式執行過程中是否能獲得處理機執行以及以怎樣速度執行,並不是程式自身能控制的,此即程式的 非同步性

臨界資源和臨界區

在計算機中有許多資源一次只能允許一個程式使用,如果多個程式同時使用則可能造成系統的混亂,這些資源被稱作 臨界資源(比如印表機)。多個程式同時使用臨界資源會使其結果具有不可再現性。

在每個程式中,訪問臨界資源的那段程式碼稱作 臨界區。為了使程式能有效共享臨界資源,並使程式的執行具有可再現性,系統必須保證程式互斥的使用臨界資源,即保證他們互斥的進入臨界區。因此,必須在臨界區前面加一段程式碼,用來檢查對應的臨界資源是否正被其他程式訪問,這段程式碼稱為 進入區;相應的在臨界區後邊也要加一段程式碼稱為 退出區,用於將臨界區從正在被訪問的標誌恢復為未被訪問的標誌。

同步機制應該遵循的規則

用來實現互斥的共享機制必須遵守以下四個規則:
1) 空閒讓進:臨界區空閒時,應允許一個請求進入臨界區的程式立即進入臨界區,以便有效的利用資源。
2) 忙則等待:當臨界資源正在訪問時,其他要求進入臨界區的程式必須等待,以保證對臨界資源的互斥訪問。
3) 有限等待:任何要求訪問臨界資源的程式應能在有限時間內進入臨界區。
4) 讓權等待:不能進入臨界區的程式應立即釋放CPU資源,以免“忙等”。

2.訊號量機制

介紹:訊號量機制是荷蘭學者Dijkstra在1965年提出的一種同步機制。
本篇文章注重介紹整型訊號量和記錄型訊號量。12
整型訊號量

最初由Dijkstra把整型訊號量定義為一個用於表示資源數目的整型量S,除了初始化外,它僅能透過兩個標準的原子操作wait(P)和signal(V)來訪問。演算法如下:

wait(S){
    while(S <= 0); 
    	*表示資源正被佔用,迴圈等待(不遵循“讓權等待”的準則
    						而是使之處於忙等狀態)*
    S--;
}
signal(S){
    S++;
}12345678910
記錄型訊號量

記錄型訊號量除了需要一個用於代表資源數目的整型變數value外,還增加了一個程式連結串列指標list,用於連結所有等待該資源的程式。其演算法如下:

typedef struct{
    int value;
    struct process_control_block *list;
} semaphore;
wait和signal操作可描述為:
wait(semaphore *S){
    S->value--;
    			*wait申請資源,資源數減1*
    if(S->value < 0)    
   				*S->value自減之後<0則表示系統中已無資源分配*
        block(S->list);
        		*block自我阻塞,放棄CPU,並插入到S->list中*
} 
signal(semaphore *S){
    S->value++;
    			*釋放資源,value加1*
    if(S->value <= 0)
    			*value加1之後value仍然<=0,表示仍然有等待該資源
    			的程式被阻塞,所以用wakeup將等待連結串列中的第一
    			個程式喚醒*
        wakeup(S->value);
}123456789101112131415161718192021222324

3.訊號量應用

利用訊號量實現前趨關係

前趨關係在上一篇文章已將這裡就不多說,直接上圖寫關係 在這裡插入圖片描述
程式碼框架如下:

P1(){signal(a);}
p2(){wait(a); signal(b); signal(c);}
P3(){wait(b); wait(d); signal(e);}
P4(){wait(c); signal(d); signal(f);}
P5(){wait(f); wait(e);}12345
利用訊號量實現互斥

為了使多個程式能互斥地訪問某個臨界資源,只需為該資源設定一互斥訊號量mutex ,並將其訊號量設定為1,然後將訪問該臨界區的資源放wait(mutex)和signal(mutex)之間。下面演算法描述瞭如何利用訊號量實現程式P1和P2的互斥:

semaphore mutex = 1;
p1(){
    while(1){
        wait(mutex);
        臨界區
        signal(mutex)
        剩餘區
    }
    
}
p2(){
    while(1){
        wait(mutex);
        臨界區
        signal(mutex)
        剩餘區
    }
}123456789101112131415161718

在利用訊號量機制實現程式互斥時需要注意,wait(mutex)和signal(mutex)必須成對出現,(成對出現不一定在一個程式中成對出現)缺少wait(mutex)將會導致系統混亂,不能保證對臨界資源的互斥訪問;而缺少signal(mutex)j將會使臨界資源永遠不被釋放,從而使因等待該資源的程式不能被喚醒。

4.經典程式的同步問題

生產者—消費者問題(個人理解)

(一個緩衝區的就不說了)如果一個生產者一個消費者n個緩衝區:
設定三個訊號量:
互斥訊號量 mutex,用於實現對緩衝池的互斥訪問,初始值為1;
資源訊號量 empty,用來表示空閒緩衝區的數量,初始值n;
資源訊號量 full,用來表示滿緩衝區的數量,初始值為0;
empty和full用來同步生產者和消費者程式。
具體演算法描述如下:

int in = 0,out = 0;
item buffer[n]
semaphore mutex = 1, empty = n, full = 0;
//生產者
void proceducer(){
    do{
        produce an item nextp;
            ...
        wait(empty);   
         //判斷是否有空閒緩衝池,並申請資源,如果有,則empty(空緩衝區)減一
        wait(mutex)         
         //申請緩衝區資源,並進入緩衝區(準備進行操作)
        buffer[in] = nextp;
        //將產品放入緩衝區
        in = (in + 1) % n;
        signal(mutex);
        //釋放緩衝區資源
        signal(full);
        //滿緩衝區數量加1,釋放資源
    }while(TRUE);
}
//消費者
void consumer(){
    do{
        wait(full);        
         //判斷是否有滿緩衝區,並申請資源,如果有,則full空緩衝區)減一
        wait(mutex);         
         //申請緩衝區資源,並進入緩衝區(準備進行操作)
        
        nextc = buffer[out];
        //拿出產品
        out = (out + 1) % n;
        signal(mutex) 
        //釋放緩衝區資源
        signal(empty);
        //空緩衝區數量加1,釋放資源
        consume the item in nextc;
            ...
    }while(TRUE);
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

注:n個緩衝池用迴圈佇列
當(in + 1)% n = out,表示進來一圈了,也就是緩衝池滿了;
當in = out表示緩衝池空;

讀者—寫者問題(個人理解)

題述:一個資料物件,如筆記或者記錄,能被多個程式共享,可把那些只要求讀資料物件的程式稱為"讀者",其他程式則稱為寫者。顯然讀者可同時讀一個共享物件,但不允許讀者和寫著同時訪問共享物件,也不允許多個寫著同時訪問共享物件。
如考慮讀者優先,即除非有讀者在讀,或者讀者就不用等待,該問題演算法可描述為:
互斥訊號量mutex:實現Reader和Writer程式間在讀或者寫時的互斥。
互斥訊號量rmutex:實現對readcount互斥訪問。
整型變數readcount:記錄讀者數量。

semaphore rmutex = 1, mutex = 1;
int readcount = 0;
//讀者
void reader(){
    do{
        //進讀者
        wait(rmutex); 
        //判斷readcount資源是否被佔用,若無,則申請資源
        if(readcount == 0) wait(mutex); 
        //如果沒有讀者,則先申請讀寫緩衝區的資源
        readcount ++;
        //讀者數量加1
        signal(rmutex)
        //釋放對readcount資源的佔用
        ...
        perform read operation;
        ...
        //出讀者
        wait(rmutex);
        //判斷readcount資源是否被佔用,若無,則申請資源
        readcount --;
        //讀者數量減1
        if(readcount == 0) signal(mutex);
        //如果讀者出完,則把對讀寫緩衝區的資源釋放    
        signal(rmutex);
        //釋放對readcount資源的佔用
    }while(TRUE);
}
//寫者
void writer(){
    do{
        wait(mutex);
        //寫者只能有一個,不需要在申請對readcount的佔用
        //所以直接申請讀寫緩衝區資源
        perform write operation;
        signal(mutex);
        //釋放對讀寫緩衝區的佔用
    }while(TRUE);
}1234567891011121314151617181920212223242526272829303132333435363738394041424344
生產者—消費者問題變形(個人理解)

問題描述:桌子上有能盛的下5個水果的空盤子,爸爸不停的向盤子中放蘋果或者橘子,兒子不停的從盤子中取走橘子,女兒不停從盤中取出蘋果,規定三人不能同時從盤子中取放水果。試用訊號量實現爸爸,兒子和女兒這三個迴圈程式之間的同步。
本題是生產者—消費者問題的變形,為一個生產者,多個緩衝區,多個消費者。
需要設定兩個不同的full變數orange和apple初始值為0。
三人不能同時取放水果,設定互斥訊號量mutex以實現三人對臨界資源的互斥訪問。
還需設定一個empty記錄盤子中的空位置(五個位置)

該問題演算法可描述為:

semaphore empty = 5, orange = 0, apple = 0, mutex = 1;
Dad(){
    do{
        wait(empty);
        //判斷是否還有空位置,若有,申請相應資源
        wait(mutex);
        //判斷是否臨界資源被其他(兒子,女兒)程式佔用
        //若沒有,這申請資源,進入緩衝區,被佔用則等待
        放水果操作
        signal(mutex);
        //釋放對臨界資源的佔用
        if(orange) signal(orange)
        else signal(apple)
        //如果放入的是橘子,則橘子數量加1,釋放orange
        //否則對apple進行操作
    }while(TRUE)
}
Daughter(){
    do{
        wait(apple)
        //判斷盤子裡是否有蘋果,如果有,則申請相應資源
        wait(mutex)
        //申請進入緩衝區,mutex減1,資源被Daughter佔用,準備對蘋果進行操作
        拿走蘋果
        signal(mutex)
        //拿走蘋果之後,把佔用的臨界資源釋放
        signal(empty)
        //蘋果被拿走,空位置(緩衝區)加1
    }while(TRUE)
}
Son(){
    do{
        wait(orange)
        //判斷盤子裡是否有橘子,如果有,則申請相應資源,orange減1
        wait(mutex)
        //申請進入緩衝區,mutex減1,資源被Son佔用,準備對橘子進行操作
        拿走蘋果
        signal(mutex)
        //拿走橘子之後,把佔用的臨界資源釋放
        signal(empty)
        //橘子被拿走,空位置(空緩衝區)加1
    }while(TRUE)
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051

注:以上僅為個人理解,如有錯誤,還望提出,佔佔積極改正!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2733238/,如需轉載,請註明出處,否則將追究法律責任。

相關文章