1. 程式人生 > >【OS】訊號量機制

【OS】訊號量機制

儲存一下自己看,侵刪。

原文地址:http://blog.csdn.net/speedme/article/details/17597373

上篇部落格中(程序同步之臨界區域問題及Peterson演算法),我們對臨界區,臨界資源,鎖機制詳細解讀了下,留下了一個問題,就是鎖機制只能判斷臨界資源是否被佔用,所以他解決了互斥問題,但是他不能確定前面的程序是否完成,所以他不能用於同步問題中。下面就為你講解訊號量機制是如何解決這一問題的。

1.訊號量機制

訊號量機制即利用pv操作來對訊號量進行處理。  

 原文地址:http://blog.csdn.net/speedme/article/details/17597373

什麼是訊號量?訊號量(semaphore)的資料結構為一個值和一個指標,指標指向等待該訊號量的下一個程序。訊號量的值與相應資源的使用情況有關。

當它的值大於0時,表示當前可用資源的數量;

當它的值小於0時,其絕對值表示等待使用該資源的程序個數。

注意,訊號量的值僅能由PV操作來改變。

      一般來說,訊號量S³0時,S表示可用資源的數量。執行一次P操作意味著請求分配一個單位資源,因此S的值減1;當S<0時,表示已經沒有可用資源,請求者必須等待別的程序釋放該類資源,它才能執行下去。而執行一個V操作意味著釋放一個單位資源,因此S的值加1;若S£0,表示有某些程序正在等待該資源,因此要喚醒一個等待狀態的程序,使之執行下去。
 

2.PV操作

什麼是PV操作?

p操作(wait):申請一個單位資源,程序進入

經典虛擬碼

wait(S){  
    while(s<=0)<span style="white-space:pre"> </span>//如果沒有資源則會迴圈等待;  ; S-- ; } 

v操作(signal):釋放一個單位資源,程序出來

signal(S){  
    S++ ;  
} 
p操作(wait):申請一個單位資源,程序進入 v操作(signal):釋放一個單位資源,程序出來 PV操作的含義:PV操作由P操作原語和V操作原語組成(原語是不可中斷的過程),對訊號量進行操作,具體定義如下:
    P(S):①將訊號量S的值減1,即S=S-1;
           ②如果S<=0,則該程序繼續執行;否則該程序置為等待狀態,排入等待佇列。
    V(S):①將訊號量S的值加1,即S=S+1;
           ②如果S>0,則該程序繼續執行;否則釋放佇列中第一個等待訊號量的程序。
  PV操作的意義:我們用訊號量及PV操作來實現程序的同步和互斥。PV操作屬於程序的低階通訊。

 

 

使用 PV操作實現程序互斥時應該注意的是:
    (1)每個程式中使用者實現互斥的P、V操作必須成對出現,先做P操作,進臨界區,後做V操作,出臨界區。若有多個分支,要認真檢查其成對性。
    (2)P、V操作應分別緊靠臨界區的頭尾部,臨界區的程式碼應儘可能短,不能有死迴圈。
    (3)互斥訊號量的初值一般為1。
 

3.三個經典同步問題

前面我們講到訊號量機制,下面我們講解利用訊號量及PV操作解決幾個經典同步問題。

a.生產者-消費者(緩衝區問題)

生產者一消費者問題(producer-consumerproblem)是指若干程序通過有限的共享緩衝區交換資料時的緩衝區資源使用問題。假設“生產者”程序不斷向共享緩衝區寫人資料(即生產資料),而“消費者”程序不斷從共享緩衝區讀出資料(即消費資料);共享緩衝區共有n個;任何時刻只能有一個程序可對共享緩衝區進行操作。所有生產者和消費者之間要協調,以完成對共享緩衝區的操作。

 

生產者程序結構: 複製程式碼
do{  
     wait(empty) ;  
     wait(mutex) ;  
      
     add nextp to buffer  
      
     signal(mutex) ;  
     signal(full) ;  
}while(1) ;
複製程式碼

消費者程序結構:

複製程式碼
do{  
     wait(full) ;  
     wait(mutex) ;  
      
     remove an item from buffer to nextp  
      
     signal(mutex) ;  
     signal(empty) ;  
}while(1) ; 
複製程式碼 我們可把共享緩衝區中的n個緩衝塊視為共享資源,生產者寫人資料的緩衝塊成為消費者可用資源,而消費者讀出資料後的緩衝塊成為生產者的可用資源。為此,可設定三個訊號量:full、empty和mutex。其中:full表示有資料的緩衝塊數目,初值是0;empty表示空的緩衝塊數初值是n;mutex用於訪問緩衝區時的互斥,初值是1。實際上,full和empty間存在如下關係:full + empty = N   注意:這裡每個程序中各個P操作的次序是重要的。各程序必須先檢查自己對應的資源數在確信有可用資源後再申請對整個緩衝區的互斥操作;否則,先申請對整個緩衝區的互斥操後申請自己對應的緩衝塊資源,就可能死鎖。出現死鎖的條件是,申請到對整個緩衝區的互斥操作後,才發現自己對應的緩衝塊資源,這時已不可能放棄對整個緩衝區的佔用。如果採用AND訊號量集,相應的進入區和退出區都很簡單。如生產者的進入區為Swait(empty,mutex),退出區為Ssignal(full,mutex)。

b.作者讀者問題

讀者一寫者問題(readers-writersproblem)是指多個程序對一個共享資源進行讀寫操作的問題。 假設“讀者”程序可對共享資源進行讀操作,“寫者”程序可對共享資源進行寫操作;任一時刻“寫者”最多隻允許一個,而“讀者”則允許多個。即對共享資源的讀寫操作限制關係包括:“讀—寫,互斥、“寫一寫”互斥和“讀—讀”允許。
我們可認為寫者之間、寫者與第一個讀者之間要對共享資源進行互斥訪問,而後續讀者不需要互斥訪問。為此,可設定兩個訊號量Wmutex、Rmutex和一個公共變數Rcount。其中:Wmutex表示“允許寫”,初值是1;公共變數Rcount表示“正在讀”的程序數,初值是0;Rmutex表示對Rcount的互斥操作,初值是1。
在這個例子中,我們可見到臨界資源訪問過程的巢狀使用。在讀者演算法中,進入區和退出區又分別嵌套了一個臨界資源訪問過程。

對讀者一寫者問題,也可採用一般“訊號量集”機制來實現。如果我們在前面的讀寫操作限制上再加一個限制條件:同時讀的“讀者”最多R個。這時,可設定兩個訊號量Wmutex和Rcount。其中:Wmutex表示“允許寫”,初值是¨Rcount表示“允許讀者數目”,初值為R。為採用一般“訊號量集”機制來實現的讀者一寫者演算法。    

c.哲學家進餐問題



(1) 在什麼情況下5 個哲學家全部吃不上飯? 
考慮兩種實現的方式,如下: 
A. 
演算法描述:  複製程式碼
void philosopher(int i) /*i:哲學家編號,從0 到4*/ { while (TRUE) { think( ); /*哲學家正在思考*/ take_fork(i); /*取左側的筷子*/ take_fork((i+1) % N); /*取左側筷子;%為取模運算*/ eat( ); /*吃飯*/ put_fork(i); /*把左側筷子放回桌子*/ put_fork((i+1) % N); /*把右側筷子放回桌子*/ } } 
複製程式碼

分析:假如所有的哲學家都同時拿起左側筷子,看到右側筷子不可用,又都放下左側筷子, 
等一會兒,又同時拿起左側筷子,如此這般,永遠重複。對於這種情況,即所有的程式都在 
無限期地執行,但是都無法取得任何進展,即出現飢餓,所有哲學家都吃不上飯。 
B. 
演算法描述: 
規定在拿到左側的筷子後,先檢查右面的筷子是否可用。如果不可用,則先放下左側筷子, 
等一段時間再重複整個過程。 
分析:當出現以下情形,在某一個瞬間,所有的哲學家都同時啟動這個演算法,拿起左側的筷 
子,而看到右側筷子不可用,又都放下左側筷子,等一會兒,又同時拿起左側筷子……如此 
這樣永遠重複下去。對於這種情況,所有的程式都在執行,但卻無法取得進展,即出現飢餓, 
所有的哲學家都吃不上飯。 
(2) 描述一種沒有人餓死(永遠拿不到筷子)演算法。 
考慮了四種實現的方式(A、B、C、D): 
A.原理:至多隻允許四個哲學家同時進餐,以保證至少有一個哲學家能夠進餐,最終總會釋 
放出他所使用過的兩支筷子,從而可使更多的哲學家進餐。以下將room 作為訊號量,只允 
許4 個哲學家同時進入餐廳就餐,這樣就能保證至少有一個哲學家可以就餐,而申請進入 
餐廳的哲學家進入room 的等待佇列,根據FIFO 的原則,總會進入到餐廳就餐,因此不會 
出現餓死和死鎖的現象。 
偽碼: 

複製程式碼
semaphore chopstick[5]={1,1,1,1,1}; semaphore room=4; void philosopher(int i) { while(true) { think(); wait(room); //請求進入房間進餐 wait(chopstick[i]); //請求左手邊的筷子 wait(chopstick[(i+1)%5]); //請求右手邊的筷子  eat(); signal(chopstick[(i+1)%5]); //釋放右手邊的筷子 signal(chopstick[i]); //釋放左手邊的筷子 signal(room); //退出房間釋放訊號量room  } } 
複製程式碼

B.原理:僅當哲學家的左右兩支筷子都可用時,才允許他拿起筷子進餐。 
方法1:利用AND 型訊號量機制實現:根據課程講述,在一個原語中,將一段程式碼同時需 
要的多個臨界資源,要麼全部分配給它,要麼一個都不分配,因此不會出現死鎖的情形。當 
某些資源不夠時阻塞呼叫程序;由於等待佇列的存在,使得對資源的請求滿足FIFO 的要求, 
因此不會出現飢餓的情形。 
偽碼: 

複製程式碼
semaphore chopstick[5]={1,1,1,1,1}; void philosopher(int I) { while(true) { think(); Swait(chopstick[(I+1)]%5,chopstick[I]); eat(); Ssignal(chopstick[(I+1)]%5,chopstick[I]); } } 
複製程式碼

方法2:利用訊號量的保護機制實現。通過訊號量mutex對eat()之前的取左側和右側筷 
子的操作進行保護,使之成為一個原子操作,這樣可以防止死鎖的出現。 
偽碼: 

複製程式碼
semaphore mutex = 1 ;   
semaphore chopstick[5]={1,1,1,1,1}; void philosopher(int I) { while(true) { think(); wait(mutex); wait(chopstick[(I+1)]%5); wait(chopstick[I]); signal(mutex); eat(); signal(chopstick[(I+1)]%5); signal(chopstick[I]); } } 
複製程式碼

C. 原理:規定奇數號的哲學家先拿起他左邊的筷子,然後再去拿他右邊的筷子;而偶數號 
的哲學家則相反.按此規定,將是1,2號哲學家競爭1號筷子,3,4號哲學家競爭3號筷子.即 
五個哲學家都競爭奇數號筷子,獲得後,再去競爭偶數號筷子,最後總會有一個哲學家能獲 
得兩支筷子而進餐。而申請不到的哲學家進入阻塞等待佇列,根FIFO原則,則先申請的哲 
學家會較先可以吃飯,因此不會出現餓死的哲學家。 
偽碼: 

複製程式碼
semaphore chopstick[5]={1,1,1,1,1}; void philosopher(int i) { while(true) { think(); if(i%2 == 0) //偶數哲學家,先右後左。  { wait (chopstick[ i + 1 ] mod 5) ; wait (chopstick[ i]) ; eat(); signal (chopstick[ i + 1 ] mod 5) ; signal (chopstick[ i]) ; } Else //奇數哲學家,先左後右。  { wait (chopstick[ i]) ; wait (chopstick[ i + 1 ] mod 5) ; eat(); signal (chopstick[ i]) ; signal (chopstick[ i + 1 ] mod 5) ; } } } 
複製程式碼

D.利用管程機制實現(最終該實現是失敗的,見以下分析): 
原理:不是對每隻筷子設定訊號量,而是對每個哲學家設定訊號量。test()函式有以下作 
用: 
a. 如果當前處理的哲學家處於飢餓狀態且兩側哲學家不在吃飯狀態,則當前哲學家通過 
test()函式試圖進入吃飯狀態。 
b. 如果通過test()進入吃飯狀態不成功,那麼當前哲學家就在該訊號量阻塞等待,直到 
其他的哲學家程序通過test()將該哲學家的狀態設定為EATING。 
c. 當一個哲學家程序呼叫put_forks()放下筷子的時候,會通過test()測試它的鄰居, 
如果鄰居處於飢餓狀態,且該鄰居的鄰居不在吃飯狀態,則該鄰居進入吃飯狀態。 
由上所述,該演算法不會出現死鎖,因為一個哲學家只有在兩個鄰座都不在進餐時,才允 
許轉換到進餐狀態。 
該演算法會出現某個哲學家適終無法吃飯的情況,即當該哲學家的左右兩個哲學家交替 
處在吃飯的狀態的時候,則該哲學家始終無法進入吃飯的狀態,因此不滿足題目的要求。 
但是該演算法能夠實現對於任意多位哲學家的情況都能獲得最大的並行度,因此具有重要 
的意義。 
偽碼: 

複製程式碼
#define N 5 /* 哲學家人數*/   
#define LEFT (i-1+N)%N /* i的左鄰號碼 */   
#define RIGHT (i+1)%N /* i的右鄰號碼 */   
typedef enum { THINKING, HUNGRY, EATING } phil_state; /*哲學家狀態*/ monitor dp /*管程*/ { phil_state state[N]; semaphore mutex =1; semaphore s[N]; /*每個哲學家一個訊號量,初始值為0*/ void test(int i) { if ( state[i] == HUNGRY &&state[LEFT(i)] != EATING && state[RIGHT(i)] != EATING ) { state[i] = EATING; V(s[i]); } } void get_forks(int i) { P(mutex); state[i] = HUNGRY; test(i); /*試圖得到兩支筷子*/ V(mutex); P(s[i]); /*得不到筷子則阻塞*/ } void put_forks(int i) { P(mutex); state[i]= THINKING; test(LEFT(i)); /*看左鄰是否進餐*/ test(RIGHT(i)); /*看右鄰是否進餐*/ V(mutex); } } 
複製程式碼

哲學家程序如下: 

複製程式碼
void philosopher(int process)  
{   
    while(true) { think(); get_forks(process); eat(); put_forks(process); } } 
複製程式碼 看完上面想必大家有點轉暈了,來幾道題,熟悉下。    

【例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)

 

1、測量控制系統中的資料採集任務把所採集的資料送一單緩衝區;計算任務則 從該緩衝區中取出資料並進行計算。試寫出利用訊號量機制實現兩者共享單緩衝區的同步演算法。

       Var Sempty,Sfull: semaphore:= 1,0

Begin

              Parbegin

              Collection:begin

              repeat

                     採集一個數據;

                     wait(Sempty);

                     資料放入緩衝區;

                     signal(Sfull);

              untill false;

              end;

              Compute:begin

              repeat

                     wait(Sfull);

                     從緩衝區取出資料;

                     signal(Sempty);

                     計算;

`                    until false;

                     end;

              Parend

       End

2、有一閱覽室,共有100個座位。讀者進入時必須先在一種登記表上登記,該表為每一座位列一個表目,包括座號和讀者姓名。讀者離開時要登出掉登記內容。試用wait和signal原語描述讀者程序的同步問題。

       var mutex, readcount :semaphore := 1,100;

Begin

              Parbegin

              Process Reader:begin

              repeat

              wait(readcount);

              wait(mutex);

              <填入座號和姓名完成登記>;

              signal(mutex);

<閱讀>

              wait(mutex)

       <刪除登記表中的相關表項,完成登出>

              signal(mutex);

              signal(readcount);

              until false;

              end;

              parend;

       End;

1)、桌上有一空盤,只允許放一個水果,爸爸專向盤中放蘋果,媽媽專向盤中放桔子;女兒專吃盤中的蘋果,兒子專吃盤中的桔子;試用wait和signal原語實現爸爸、媽媽、女兒、兒子之間的同步問題。

var Sempty, Sapple, Sorange,: semaphore:= 1,0,0;

 begin

       parbegin

              Father: begin

                            repeat

                                   wait(Sempty);                                <put apple in tray>;

                                   signal(Sapple);                         until false;

                       end;

              Mother: begin

                            repeat

                                   wait(Sempty);                                <put orange in tray>;

                                   signal(Sorange);                       until false;

                        end;

              Son: begin

                            repeat

                                   wait(Sorange);

                                   <take orange>;

                                   signal(Sempty);

                            until false;

                      end;

              Daughter: begin

                            repeat

                                   wait(Sapple);

                                   <take apple>;

                                   signal(Sempty);                       until false;

                       end;

              parend;

end;

1、在4×100米接力賽中,4個運動員之間存在如下關係,運動員1跑到終點把接力棒交給運動員2;運動員2一開始處於等待狀態,在接到運動員1傳來的接力棒後才能往前跑,他跑完100米後交給運動員3,運動員3也只有在接到運動員2傳來的棒後才能跑,他跑完100米後交給運動員4,運動員4接到棒後跑完全程。請試用訊號量機制對其上過程進行分析。

        

       var s1,s2,s3:semaphpre:=0,0,0;

       begin

              parbegin

                     Athlete1: begin

                                   Run100m;

                                   signal(s1);

                                end;

                     Athlete2: begin

                                   wait(s1);

                                   Run100m;

                                   signal(s2);

                                end;

                     Athlete3: begin

                                   wait(s2);

                                   Run100m;

                                   signal(s3);

                                end;

                     Athlete4: begin

                                   wait(s3);

                                   Run100m;

                                end;

              parend;

       end

 

2、在公共汽車上,司機和售票員各行其職,司機負責開車和到站停車;售票員負責售票和開、關車門;當售票員關好車門後駕駛員才能開車行駛。試用wait和signal操作實現司機和售票員的同步。

var s1,s2:semaphore:=0,0;

begin

       parbegin

              Process Driver

              begin

              repeat

                     <go right>;

                     <stop bus>;

                     signal(s2);

                     wait(s1);

              until false;

              end;

              Process BookingClerk;

              begin

              repeat

                     <ticketing>;

                     wait(s2);

                     <open the door>;

                     <close the door>;

                     signal(s1);

              until false

              end;

       parend;

end;

1、假設有3個併發程序P,Q,R,其中P負責從輸入裝置上讀入資訊,並傳送給Q,Q將資訊加工後傳