1. 程式人生 > >作業系統(2.3程序同步)本章最後結合記錄型訊號量的使用方法和例題進行了詳細講解。

作業系統(2.3程序同步)本章最後結合記錄型訊號量的使用方法和例題進行了詳細講解。

最近在準備推免的面試把王道的程序這一章拿出來做了一下,收穫挺多的,寫個文章總結下

 2.3程序同步

訪問臨界資源過程

do{

entry section;//進入區 設定訪問臨界區標誌

critical section;//臨界區 訪問臨界資源

exit section;//退出區 將訪問臨界區標誌清除

remainder section;//剩餘區 

}while(true)

軟體實現臨界區互斥

1.單標誌法

單標誌法主要問題是對臨界區資源的訪問各個程序需要交替進行,如果一個程序完成後,及時臨界區可以訪問,另一個程序也無法進入。這就違背了“空閒讓進”原則

P0程序:
while(turn!=0);
critical section;
turn=1;
remainder section;
p1程序:
while(turn!=1);
critical section;
turn =0;
remainder section;

2.雙標誌法先檢查

先判斷對方是否在訪問臨界區,然後再決定自己是否進入,它都問題是如果按照1 2 3 4的順序執行的話,雙法都進入臨界區,這樣就違背了忙則等待的原則

pi程序
while(flag[j));//1
flag[i]=TRUE;//3
critical section;
flag[i]=FALSE;
remainder section;

pj程序
while(flag[i]);//2
flag[j]=TRUE;//4
critical section;
flag[j]=FALSE;
remainder section;

3.雙標誌後檢查法

先將臨界區設定為自己的,再判斷對方是否訪問,這個方法的問題是如果按照1 2 3 4 執行的話,3,4 都會永遠處於真,這樣會導致飢餓問題

pi程序
flag[i]=TRUE;//1
while(flag[j]);//3
critical section;
flag[i]=FALSE;
remainder section;

pj程序
flag[j]=TRUE;//2
while(flag[i]);//4
critical section;
flag[j]=FALSE:
remainder section;

4.Peterson‘s Algorithm

該演算法的核心是通過增加turn讓後執行的turn賦值語句,決定誰先進入臨界區

pi程序
flag[i]=TRUE;//1
turn=j;
while(flag[j]&&turn==j);//3
critical section;
flag[i]=FALSE;
remainder section;

pj程序
flag[j]=TRUE;//2
turn=i;
while(flag[i]&&turn==i);//4
critical section;
flag[j]=FALSE:
remainder section;


硬體實現方法

1.中斷遮蔽方法

因為cpu只有在發生中斷的時候才能進行程序切換,因此關中斷就可以保證臨界區資源順利訪問

2.硬體指令

TestAndSet指令

Swap 指令

優點:適用於任意數目的程序,不管是單處理機還是多處理機:簡單,容易驗證其正確性,可以支援程序內有多個臨界區,只需為沒給臨界區設立一個訊號量即可。

缺點:程序等待進入臨界區時需要消耗處理機時間,不能實現讓權等待,從等待進入程序中隨機選擇一個進入臨界區,有的程序可能一直選不上,從而導致”飢餓“現象。

訊號量

訊號量只能被兩個標準的原語wait(S)和signal(S)來訪問,也可被記錯P/V

1.整型訊號量

一個用於表示資源數目的整型量S,wait和signal操作可描述為下方,問題是wait中如果S<=0,就會不斷嘗試,因此該機制並未遵循“讓權等待”原則

wait(S){
    while(S<=0);
    S=S-1;
}
signal(S){

S=S+1;

}

2.記錄型訊號量

記錄型訊號量通過一個記錄程序的佇列來避免重複嘗試。

記錄型訊號量資料結構

typedef struct{
    int value;
    struct process *L;
}semaphore;
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);
    }
}

重要概念

1.臨界資源:一次僅允許一個程序使用的資源。

2.臨界區:程序中用於訪問共享資源的程式碼。

3.臨界資源是互斥共享資源,非共享資料不屬於臨界資源。

4.可重入程式碼(Reentry code)也叫純程式碼(Pure code)是一種允許多個程序同時訪問的程式碼。為了使各程序所執行的程式碼完全相同,故不允許任何程序對其進行修改

5.公用佇列可供多個程序使用,但一次只可有一個程序使用。

6.P,V 操作是一種低階的程序通訊原語,它是不能被中斷的,也不是系統呼叫的。

7.執行P操作可能導致程序阻塞,但是程序在執行這個操作時還是執行狀態。

8.用P,V操作實現程序同步,訊號量的初值由使用者確定。若期望的訊息尚未產生,則置初值為 0,若期望的訊息已經存在,則置為條件中的數值,比如讀者寫者問題,empty因為已經存在置為n,full開始時不存在就置為0。

9.管程不僅能實現程序間的互斥,而且能夠實現程序間的同步。

10.原語是指完成某種功能且不被分割不被中斷執行的操作序列,通常可由硬體來實現完成不被分割執行特性的功能。在單處理機時還可以通過軟體關閉中斷的方式。

問題

1.訊號量的初始值問題?

訊號量有兩種資源訊號量和互斥訊號量,同步訊號量初始值為1,小於0時,其絕對值為在等待進入該臨界區的程序數。資源訊號量初始值為該臨界資源的數目,小於0時,其絕對值為在等待該臨界資源的程序數,大於0時,其絕對值為該臨界資源的剩餘數。

2. 管程wait/signal與PV操作

訊號量的P操作可能阻塞,也可能不阻塞;而管程的wait操作一定會阻塞。訊號量的V操作如果喚醒了其他執行緒,當前執行緒與被喚醒執行緒併發執行;對於管程的signal操作,要麼當前執行緒繼續執行,要麼被喚醒執行緒繼續執行,二者不能併發。 同時訊號量的V操作一定會改變訊號量S的值(S=S+1)。管程中的signal操作是針對某個變數的,如果不存在因該條件而阻塞的程序,signal不會產生任何影響。

訊號量的利用

1.訊號量的同步

要求P1執行X語句後才可執行P2 Y語句,通過在將S=0,和在Y語句上方放置P操作,來限制P2執行,通過在P1 X語句後放置V操作來使P2可以繼續執行,見程式碼。

semaphore S=0;
P1(){
    x;
    V(S);
}
P2(){
    P(S);
    y;
}

2.訊號量的互斥

通過將S=1和在對臨界區資源上方放置P操作來實現,因為一旦有個程序實現了P操作別的程序就P操作就會組織他們進入臨界區只能等第一個程序的V操作

semaphore S=1;
P1(){
    P(S);
    臨界區;
    V(S);
}

P2(){
    P(S);
    臨界區;
    V(S);
}

3 訊號量的前驅圖(同步的加強版)

只需根據每個節點的出度入度即可得知P/V操作的數量,同時每條邊都是一個同步關係需要設定一個訊號量,出度是V操作,入度是P操作,參考案例

S1->S2  a1

S1->S3 a2

S2->S4 b1

S2->S5 b2

S3->S6 c

S4->S6 d

S5->S6 e

semaphore a1=a2=b1=b2=c=d=e=0;
S1(){
    ...;
    V(a1);V(a2);
}
S2(){
    P(a1);
    ...;
    V(b1);V(b2);
}

S3(){
    P(a2);
    ...;
    V(c);
}

S4(){
    P(b1);
    ...;
    V(d);
}

S5(){
    P(b2);
    ...;
    V(e);
}


S6(){
    P(c);
    P(d);
    P(e);
    ...;
}

學習了訊號量的利用讓我們將他們融匯貫通吧

經典同步問題

1.生成者消費者問題

問題描述

一組生產者程序和一個消費者程序共享一個初始為空,大小為n的緩衝區,只有緩衝區沒滿時,生產者才能把訊息放入到緩衝區,否則必須等待;只有緩衝區不空時,消費者才能從中取出訊息,否則必須等待。由於緩衝區是臨界資源,它只允許一個生產者放入訊息,或者一個消費者從中取出訊息。

關係分析:生產者於消費者因為有先後順序,所以是同步關係,因為對緩衝區的爭奪所以也是互斥關係。

思路:設定一個mutex=1來實現對緩衝區的互斥反問,要知道緩衝區對於生產者的資源是空多少,他的初始值是empty=n,緩衝區對消費者的資源是滿多少,顧設定full=0;

semaphore mutex=1;
semaphore empty=n;
semaphore full=0;
producer(){
    while(True){
        P(empty)
        P(mutex)
        put();
        V(mutex)
        V(full);
    }
} 
consumer(){
    while(True){
        P(full)
        P(mutex)
        put();
        V(mutex)
        V(empty);
    }
}    
     
    

註解:有的人可能會問為什麼mutex要在full或者empty的裡面,可以這麼理解,他們各自都必須先確定自己可以對緩衝區操作才能佔用緩衝區,否則會出現死鎖,比如一開始對消費者如果mutex先鎖定緩衝區,然後p(full)發現緩衝區為空被阻塞,但因為他對緩衝區的鎖定使得生成者也沒有機會進入,導致死鎖。

生產者-消費者變種題1

問題描述:桌子上有一隻盤子,每次只能向其中放入一個水果,爸爸專門向盤子中放入蘋果,媽媽專門向盤子中放入橘子,兒子專等吃盤子中的橘子,女兒專等吃盤子中的蘋果。只有盤子為空時,爸爸或媽媽就可向盤子中放一個蘋果,僅當盤子中的有自己需要的水果時,兒子或女兒可以從盤子中取出。

關係分析:爸爸媽媽之間對盤子是互斥的,爸爸女兒和媽媽兒子是同步的

思路分析:設定mutex=1互斥訪問盤子,設定emptyA=1,fullA=0,同步爸爸女兒,設定emptyB=1,fullB=0,同步媽媽兒子。

semaphore mutex=1;
semaphore emptyA=1,fullA=0;
semaphore emptyB=1,fullB=0;
dad(){
    while(True){
        P(emptyA)
        P(mutex)
        putApple();
        V(fullA);
    }
}
Mom(){
    while(True){
        P(emptyB);
        P(mutex);
        putJuice();
        V(fullB);
    }
}
daughter(){
    while(True){
        P(fullA);
        getApple();
        V(mutex);
        V(emptyA);
    }
}
son(){
    while(True){
        P(fullB);
        getJuice();
        V(mutex);
        V(emptyB);
    }
}

上方為標準解法但是仔細想想是可以簡化的,因為mutex等價於empty,因為如果獲得盤子權力,那必是兒子,女兒已經拿掉了,通過V操作喚醒的,那盤子必定是空,可以放入無需檢查,因此去掉兩個empty仍然正確。

 semaphore mutex=1;
semaphore emptyA=1,fullA=0;
semaphore emptyB=1,fullB=0;
dad(){
    while(True){
        P(mutex)
        putApple();
        V(fullA);
    }
}
Mom(){
    while(True){
        P(mutex);
        putJuice();
        V(fullB);
    }
}
daughter(){
    while(True){
        P(fullA);
        getApple();
        V(mutex);
    
    }
}
son(){
    while(True){
        P(fullB);
        getJuice();
        V(mutex);
       
    }
}     

生產者-消費者變種問題2

(2009年計算機聯考真題)三個程序P1,P2,P3互斥使用一個包含N個單元的緩衝區,P1每次用produce()生成一個正整數並用put()送入緩衝區某一空單元中;P2每次用getodd()從該緩衝區中取出一個奇數並用countodd()統計奇數個數;P3每次用geteven()從該緩衝區中取出一個偶數並使用counteven()統計偶數個數,請使用訊號量機制實現三個程序的同步與互斥活動,並說明所定義的訊號量的含義。

關係分析:P1與P2/P3是同步,他們之間因為對緩衝區的爭奪是互斥

思路分析:設定mutex=1實現對緩衝區的互斥訪問,設定empty=n和full=0來實現同步。

semaphore mutex=1;
semaphore empty=n;
semaphore pA=0;
semaphore pB=0;
P1(){
    while(True){
        P(empty);
        x=produce();
        P(mutex);
        put();
        V(mutex);
        if(x%2==0){
            V(pB)
        }else{
            V(pA);
        }
    }
}

P2(){
    while(True){
        P(pA);
        P(mutex);
        getodd();
        V(mutex);
        V(empty)
        countodd();
    }
}

P3(){
    while(True){
        P(pB);
        P(mutex);
        geteven();
        V(mutex);
        V(empty);
        counteven();
    }
}

生產者-消費者變種問題3

在一個倉庫中可以存放A和B兩種產品,要求:

(1)每次只能存入一種產品。

(2)A產品數量-B產品數量<M

  (3)   B產品數量-A產品數量<N

其中M,N是正整數,試用P操作,V操作描述產品A與產品B的入庫過程。

思路分析:A最多能生產的數量肯定是M-1,B最多能生產的數量最多肯定是N-1,同時A生成

semaphore AB=M-1;
semaphore BA=N-1;
semaphore mutex=1;
P1(){
    while(True){
        P(AB)
        P(mutex)
        A();
        V(mutex)
        V(BA);
    }
}
P2(){
    while(True){
        P(BA)
        P(mutex)
        B();
        V(mutex)
        V(AB);
    }
}

生產者-消費者變種問題4

某個工廠有兩個生產車間和一個裝配車間,兩個生產車間分別生產A,B兩種零件,裝配車間的任務是把A,B兩種零件組裝成產品。兩個生產車間每生產一個零件後都要分別把它們送到裝配車間F1,F2上,F1存放零件A,F2存放零件B,F1和F2的容量均可以存放10個零件。裝配工人每次從貨架上取出一個零件A和一個零件B後組裝成產品。請用P,V操作進行正確管理

關係分析:裝配車間為消費者,生產A,B兩種零件的是車間是生產者。

思路分析:這道題唯一不同就是多了一個組裝,騎士只要把獲取兩種產品的過程寫在一起就是組裝了,mutexA=1,mutexB=1,對車間的互斥訪問,emptyA=10,emptyB=10,fullA=0,fullB=0。

semaphore emptyA=10,emptyB=10;
semaphore fullA=0,fullB=0;
semaphore mutexA=1,mutexB=1;
PA(){
    while(True){
        P(emptyA);
        P(mutexA);
        putA();
        V(mutexA);
        V(fullA);
    }
}
PB(){
    while(True){
        P(emptyB);
        P(mutexB);
        putB();
        V(mutexB);
        V(fullB);
    }
}
PC(){
    while(True){
        P(fullA);
        P(mutexA);
        getA();
        V(mutexA);
        V(emptyA);
        
        P(fullB);
        P(mutexB);
        getB();
        V(mutexB);
        V(emptyB);
        //組裝
    }
}

生產者消費者變種問題5

某寺廟,有小和尚,老和尚若干,有一水缸,由小和尚提入水缸供老和尚飲用,水缸可容納10桶水,水取自同一井中,水井徑窄,每次只能容納一桶取水,水桶總數為3個,每次入缸取水僅為1桶水,且不可同時進行。試給出有關從缸取水,入水的演算法描述。

關係分析:小和尚和老和尚之間喝水為同步,同時對水缸為互斥,對桶進行爭奪,小和尚之間對井的爭奪也是互斥

思路分析:井互斥mutexJ=1;缸互斥mutexG=1,桶資源mutex=3;full=0,empty=10;

semaphore mutexJ=1,mutexG=1,Tong=3,full=0,empty=10;
Psmall(){
    while(True){
        P(empty);
        P(Tong)
        P(mutexJ);
        //取水
        P(mutexJ);
        P(mutexG);
        put();
        V(mutexG);
        V(full);
        V(Tong);
    }
}
Podd(){
    while(True){
        P(full);
        P(Tong);
        P(mutexG);
        get();
        V(mutexG);
        V(empty);
        //喝水
        V(Tong);
    }
}

生產者消費者變種問題6

設P,Q,R共享一個緩衝區(該緩衝區大小為1),P,Q構成一對生產者-消費者,R既為生產者又為消費者,使用P,V操作實現其同步。

思路分析:R既為消費者又為生產者,則必須在執行前判斷狀態,若empty=1,則執行生產者功能,若full==1,執行消費者功能

semaphore full=0;
semaphore empty=1;
semaphore mutex=1;
Procedure P
{
    while(TRUE){
        P(empty);
        P(mutex);
        put();
        V(mutex);
        V(full);
    }
}

Procedure Q
{
    while(TRUE){
        P(full);
        P(mutex);
        get();
        V(mutex);
        V(empty);
    }
}


Procedure R
{
    while(TRUE){
    if(empty==1){
        P(empty);
        P(mutex);
        put();
        V(mutex);
        V(full);
    }
    if(full==1){
        P(full);
        P(mutex);
        get();
        V(mutex);
        V(empty);
    }
}

生產者消費者變種問題7

設自行車生產線上有一隻箱子,其中有N個位置(N>=3),若每個位置可存放一個車架或一個車輪,又設有三個工人,其活動分別為:

工人1活動:

do{

加工一個車架;

車架放入箱中;

}while(1)

工人2活動:

do{

加工一個車輪;

車架放入箱中;

}while(1)

工人3活動:

do{

箱中取出一個車架;

箱中取二個車輪;

組裝為一臺車

}while(1)

試分別用訊號量宇PV操作實現三個工人的合作,要求解中不含死鎖

思路分析:本體需要知道一個限制雖然成產線有N個位置但是車輪的最大數目卻是N-1,車架是N-2,否則會出現死鎖。同時需要設定一個mutex=1來互斥訪問緩衝區,設定

semaphore mutex=1;
semaphore empty1=N-2;
semaphore full1=0;
semaphore empty2=N-1;
semaphore full2=0;
semaphore empty=N;
do{
    P(empty1);
    P(empty);
    加工一個車架;
    P(mutex);
    車架放入箱中;
    V(mutex);
    V(full1);

}while(1)

工人2活動:

do{
    P(empty2);
    P(empty);
    加工一個車輪;

    P(mutex);
    車輪放入箱中;
    V(mutex);
    V(full2);

}while(1)

工人3活動:

do{
    P(full1);
    P(mutex);
    箱中取出一個車架;
    V(mutex);
    V(empty);
    V(empty1);

    P(full2);
    P(full2);
    P(mutex);
    箱中取一個車輪;
    V(mutex);
    V(empty);
    V(empty);
    V(empty2);
    V(empty2);


    組裝為一臺車

}while(1)

註釋:本體沒有強制要求去拿互斥,可以去掉mutex

生產者消費者變種問題8

(2015年計算機聯考題)有A,B兩個人通過信箱進行辯論,每個人都從自己的信箱中國呢取得對方的問題。將答案和向對方提出的新問題組成一個郵件放入對方的郵箱中。假設A的信箱最多放M個郵件,B的信箱最多放N個郵件,初始時A的信箱中有x個郵箱(0<x<M),B的信箱中有y個(0<y<N),辯論者每次取出一個郵件,郵件數減1,A和B兩個人的操作過程描述如下

CoBegin

A{

while(TRUE){

從A的信箱中取出一個郵件;

回答問題並提出一個新問題;

將新郵件放入B的信箱;

}

}

B{

while(TRUE){

從B的信箱中取出一個郵件;

回答問題並提出一個新問題;

將新郵件放入A的信箱;

}

}

當信箱不為空時,辯論者才能從信箱中取郵件,否則等待,當信箱不滿時,辯論者才能將新郵件放入信箱,否則等待。請新增必要的訊號量和P,V(或wait,signal)操作,以實現上述過程的同步,要求寫出完整步驟。

思路分析:本題是個雙向生產者消費者問題,同時要求每次要將獲得的東西整理後重新發給對方。

semaphore empty1=N-x,full1=x;
semaphore empty2=M-y,full2=y;
semaphore mutex1=1,mutex2=1;
A{

    while(TRUE){
    V(full1);
    P(mutex1);
    從A的信箱中取出一個郵件;
    V(mutex1);
    P(empty2);
    
    回答問題並提出一個新問題;
    P(mutex2);
    將新郵件放入B的信箱;
    V(mutex2);

    }

}

B{

    while(TRUE){
    V(full2);

    P(mutex2);
    從B的信箱中取出一個郵件;
    V(mutex2);

    P(empty1);

    回答問題並提出一個新問題;

    P(mutex1);
    將新郵件放入A的信箱;
    V(mutex1);

    }

}