作業系統(6)同步、通訊與死鎖

笨蛋糕發表於2014-03-03

1、併發程式:

程式的併發性是指一組程式的執行在時間上是重疊的,所謂時間重疊是指一個程式執行第一條指令是在另一個程式執行完最後一條指令之前開始的。

程式的互動必須是有控制的,否則會出現不正確的計算結果。併發程式的無關性是程式的執行與時間無關的一個充分條件。

由於一個程式的執行速度通常無法為另一個程式所知,對於共享公共變數(資源)的併發程式來說,計算結果往往取決於這一組併發程式執行的相對速度。可能出現與時間有關的錯誤,一種形式是結果不唯一,如飛機票售票問題。另一種是永遠等待,如主存管理問題。

2、競爭關係:

即互斥關係。作業系統必須協調程式對公共資源的爭用。否則會引起兩個控制問題:一是死鎖,一組程式如果都獲得部分資源,還想要得到其他程式鎖佔有的資源,最終所有程式將陷入永遠等待的狀態。二是飢餓,一個可執行程式由於其他程式總是優於它,而被排程程式無限期地拖延而不能被執行。

解決飢餓問題最簡單的辦法是FCFS資源分配策略。

3、協作關係:

程式同步是指為完成共同任務的併發程式基於某個條件來協調其活動,因為需要在某些位置上排定執行的先後次序而等待、傳遞訊號或訊息所產生的協作制約關係。

注意:程式互斥關係是一種特殊的程式同步關係,即逐次使用互斥共享資源,也是對程式使用資源的次序的一種協調。

4、互斥和臨界區:

併發程式中與共享變數有關的程式段稱為臨界區。共享變數所代表的資源稱為臨界資源。

臨界區的排程原則:互斥使用,有空讓進;忙則等待,有限等待;擇一而入,演算法可行。

5、Peterson演算法:

為每個程式設定標誌,當標誌值為false時表示此程式要求進入臨界區,另外再設定一個指示器turn以指示可以由哪個程式進入臨界區,當turn=i時則可由程式Pi進入臨界區。

6、實現臨界區管理的硬體設施:

1)關中斷:

實現互斥的最簡單方法是在程式進入臨界區時關中斷,在程式退出臨界區時開中斷。中斷被關掉後,時鐘中斷也被遮蔽,因為程式上下文切換都是由中斷事件引起的,這樣程式的執行再也不會被打斷。

2)測試並建立指令:

使用硬體所提供的“測試並建立”機器指令TS,可把其看作函式,它有布林型引數x和返回條件碼,當TS(&x)測到x值為true時則置x為false,且根據所測試到的x值形成條件碼。

3)對換指令:

功能是交換兩個字的內容。在Intel X86中,對換指令為XCHG,為每個臨界區設定布林型鎖變數,如lock,當其值為false時表示無程式在臨界區內。

7、生產者-消費者問題:

是典型的程式同步問題,是併發程式內在關係的一種抽象。描述如下:

有n個生產者和m個消費者,連線在具有k個單位緩衝區的有界環狀緩衝上,故又稱有界緩衝問題。其中,pi和ci都是併發程式,只要緩衝區未滿,生產者程式pi所產生的產品就可投入緩衝區;類似的,只要緩衝區非空,消費者程式cj就可從緩衝區取走並消耗產品。

//程式描述

int k;

typedef anyitem item;    //item型別

item buffer[k];

int in = 0, out = 0, counter = 0;

process producer( void) {

    while( true)   {             //無限迴圈

           { produce an item in nextp };        //生成一個產品

           if( counter == k)                      //緩衝區滿時,生產者睡眠

                 sleep( producer);

           buffer[in] = nextp;              //將一個產品放入緩衝區

            in = (in +1)%k;                 //指標推進

            counter ++;                 //緩衝區內產品數加1

            if( counter ==1)                //緩衝區空了,加進一件產品

                  wakeup(consumer);             //並喚醒消費者

       }

}

process consumer(void )  {

     while(true)  {                    //無限迴圈

          if( counter == 0)              //緩衝區為空,消費者睡眠

               sleep(consumer);

          nextc = buffer[ out];          //取一個產品到netxc

          out = (out +1)%k;          //指標推進

          counter --;            //取走一個產品,計數器減1

          if(counter == k-1)       //緩衝區滿,取走一件產品

                 wakeup(producer);             //並喚醒生產者

           { consumer the item in netxc };          //消耗產品

       }

}

8、訊號量與PV操作:

訊號量表示物理資源的實體,是一個與佇列有關的整型變數,用一個結構型資料結構表示,有兩個分量:一個是訊號量的值,另一個是訊號量佇列的指標。

1)按用途分為:

公用訊號量:聯絡一組併發程式,相關的程式均可在此訊號量上執行PV操作。

私有訊號量:聯絡一組併發程式,僅允許此訊號量所擁有的程式執行p操作,而其他相關程式可在其上施行V操作,初值為0或正整數,多用於併發程式同步。

2)按取值分為:

二元訊號量:僅允許取值為0或1,主要用於解決程式互斥問題。

一般訊號量:又稱計數訊號量,允許取值大於1的整型值,主要用於解決程式同步問題。

9、P(s):將訊號量value值減1,若結果小於0,則執行p操作的程式被阻塞,排入與s訊號量有關的list所指佇列中;若結果大於等於0,則執行p操作的程式繼續執行。

V(s):將訊號量value值加1,若結果不大於0,則執行V操作的程式從訊號量s有關的list所指佇列中釋放一個程式,使其轉換為就緒態,自己則繼續執行;若結果大於0,則執行V操作的程式繼續執行。

10、訊號量實現互斥:

P、V操作只對訊號量測試一次,而TS指令則必須反覆測試。

用訊號量和PV操作管理併發程式互斥進入臨界區的一般形式為:

semaphore mutex;

mutex = 1;

cobegin;

process Pi() {  //i = 1,2,....n

       P( mutex);

        { 臨界區};

       V( mutex);

 }

coend

11、訊號量解決5位哲學家吃通心麵問題:

semaphore fork[5];

for( int i=0; i<5; i++)

      fork[i] = 1;

cobegin

process philosopher_i( )  {   //i=0,1,2,3,4

        while( true)   {

              think( );

         P(fork[i]);

         P(fork[(i+1)%5]);

         eat( );

         V(fork[i]);

         V(fork[(i+1)%5]);

      }

}

coend

12、訊號量解決生產者-消費者問題

//單緩衝區生產者-消費者問題

int B;

semaphore empty;        //可用的空緩衝區數

semaphore full;               //緩衝區內可用的產品數

empty = 1;                   //緩衝區內允許放入一件產品

full = 0;                  //緩衝區內沒有產品

cobegin

process producer ( )  {                     process consumer ( )  {   

      while( true)  {                                    while( true)  {    

            produce( );                                        P(full);

            P(empty);                                          take( ) from B;

            append( ) to B;                                  V( emoty);

            V(full);                                                consume( );

       }                                                        }

}                                                        }

coend

 //m個生產者和n個消費者共享k件產品緩衝區問題

item B[k];

semaphore empty;   empty = k;           //可用的空緩衝區數

semaphore full;     full = 0;               //緩衝區內可用的產品數

semaphore   mutex;   mutex = 1;           //互斥訊號量

int in = 0;                //放入緩衝區指標

int out = 0;            //取出緩衝區指標

cobegin

process producer_i( )   {                      process consumer_j( )   {

          while(true)   {                                        while(true)   {

                produce( );                                           P(full);

                P(emoty);                                             P(mutex);

                P(mutex);                                              take() from B[out];

                append to B[in];                                    out = (out+1)%k;

                in = (in +1)%k;                                      V(mutex);

                V(mutex);                                               V(empty);

                V(full);                                                    consume( );

          }                                                               }

}                                                             }

coend 

程式中的P(mutex)和V(mutex)必須成對出現,夾在兩者之間的程式碼段是程式的臨界區。施加於訊號量empty和full上的P、V操作也必須成對出現,但分別位於不同的程式中。一般來說,用於互斥的訊號量上的P操作總是在後面執行。

相關文章