1. 程式人生 > >實現臨界區互斥的基本方法

實現臨界區互斥的基本方法

----------軟體實現方法:

               在進入區設定和檢查一些標誌來表明是否有程序在臨界區中,如果已有程序在臨界區,則在進入區通過迴圈檢查進行等待,程序離開臨界區後則在退出區修改

          標誌。

               (1)、演算法一:單標誌法。(違背“空閒讓進”原則)

                         該演算法設定一個公用整型變數 turn ,用於指示被允許進入臨界區的程序編號,即,若turn = 0 ,則允許p0 程序進入臨界區。該演算法可確保每次只允許一個

                     程序進入臨界區。但兩個程序必須交換進入臨界區,如果某個程序不再進入臨界區了,那麼另一個程序也將無法進入臨界區(違背“空閒讓進”)。這樣很容易

                     造成資源利用不充分。

                           假如,p0順利進入臨界區並從臨界區離開,那麼此時臨界區是空閒的,但p1 並沒有進入臨界區的打算,turn = 1 一直成立,p0 就無法再次進入臨界區了

                     (一直被 while 死迴圈困住)

                           p0 程序:

                                while(turn != 0);          //進入區

                               critical section  ;          //臨界區

                                turn = 1;                       //退出區

                                remainder section;   //剩餘區

                             p1程序:

                                 while(turn != 1);           //進入區

                                 critical section;            //臨界區

                                  turn = 0;                       //退出區

                                  remainder section;   //剩餘區

                  (2)、演算法二:雙標誌法先檢查。(違背“忙則等待”原則)

                                    該演算法的基本思想是在每一個程序訪問臨界區資源之前,先檢視一下臨界區資源是否正被訪問,若正被訪問,該程序需等待;否則,程序才進入自己

                            自己的臨界區。為此,設定一個數據  flag[ i ] ,如第i個元素值為 FALSE ,表示Pi程序未進入臨界區,值為 TRUE ,表示Pi程序進入臨界區。

                            Pi 程序:

                                    while(flag[ j ]);             //進入區

                                    flag[ i ] = TRUE;          //進入區

                                    critical section;           //臨界區

                                    flag[ i ] = FALSE;        //退出區

                                    remainder section;   //剩餘區

                            Pj程序:

                                     while(flag[ i ]);            //進入區

                                     flag[ j ] = TRUE;         //進入區

                                     critical section;          //臨界區

                                     flag[ i ] = FALSE;        //退出區

                                     remainder section;   //剩餘區

                            優點:不用交替進入,可連續使用。

                            缺點:Pi 程序和 Pj程序可能同時進入臨界區。

                                        檢查和修改操作不能一次進行。

                (3)、演算法三:雙標誌法後檢查(會導致“飢餓”現象)

                                   演算法二是先檢查對方程序狀態標誌後,再置自己的標誌,由於在檢查和放置中可插入另一個程序到達時的檢測操作,會造成兩個程序在分別檢查

                            後,同時進入臨界區。為此,演算法三採用先設定自己標誌為  TRUE  後,再檢測對方狀態標誌,若對方標誌位 TRUE,則程序等待;否則進入臨界區。

                                  Pi程序:

                                  flag[ i ] =TRUE;               //進入區

                                  while(flag[ j ]);                 //進入區

                                  critical section;               //臨界區

                                  remainder section;        //剩餘區

                                  Pj程序:

                                  flag[ j ] =TRUE;                //進入區

                                  while(flag[ i ] );                 //進入區

                                  critical section;                //臨界區

                                  remainder section;         //剩餘區

                            當兩個程序幾乎同時都想進入臨界區時,它們分別將自己的標誌值 flag 設定為  TRUE ,並且同時檢測對方的狀態(執行 while 語句),發現對方

                       也要進入臨界區,於是雙方互相謙讓,結果誰也進不了臨界區,從而導致“飢餓”現象。

                    (4)、演算法四:Peterson's Algorithm(皮特森演算法:單標誌法和雙標誌法後檢查的結合)

                                為了防止兩個程序為進入臨界區而無限期等待,又設定變數 turn ,,每個程序在設定自己標誌後再設定 turn 標誌。這時,再同時檢測另一個程序

                       狀態標誌不允許進入標誌,這樣可以保證當程序同時要求進入臨界區,只允許一個程序進入臨界區。  

                                Pi 程序:

                                flag[ i ] = TRUE; turn = j;             //進入區

                                while(flag[ j ] && turn == j);        //進入區

                                critical section;                            //l臨界區

                                flag[ i ] = FALSE ;                         //退出區

                                 remainder section;                    //剩餘區

                                Pj 程序:

                                flag[ j ] = TRUE; turn = i;             //進入區 

                                while(flag[ i ] && turn == i);        //進入區

                                critical section;                             //臨界區

                                 flag[ j ] = FALSE;                         //退出區

                                 remainder section;                     //剩餘區

                             具體如下:考慮程序  Pi ,一旦它設定 flag[ i ] = TRUE ,表示它想要進入臨界區,同時 turn = j ,此時如果程序 Pj 已經在臨界區中,則符合程序Pi 的while

                       迴圈條件,則Pi 不能進入臨界區。而如果 Pj 程序沒要進入臨界區,即  flag[ j ] = FALSE ,迴圈條件不符合,則  Pi 程序可以順利進入,反之亦然,本演算法

                       的基本思想是:演算法一和演算法三的結合。利用 flag 解決臨界資源的互斥訪問,而利用  turn 解決“飢餓”現象。

----------硬體實現方法:

                       計算機提供了特殊的硬體指令,允許對一個字中的內容進行檢測和修正,或者是對兩個字的內容進行交換等。通過硬體支援實現臨界區問題的低階方法或

                     稱為 元方法

                   (1)、中斷遮蔽方法:

                                  當一個程序正在使用處理機執行它的臨界區程式碼時,要防止其他程序再進入其臨界區訪問的最簡單的方法是:禁止一切中斷髮生,或稱之為遮蔽中斷、

                         關中斷。

    因為CPU 只在發生中斷時引起程序切換,這樣遮蔽中斷就能保證當前執行程序將臨界區程式碼順利地執行完,從而保證互斥的正確實現,然後再執行開

                          中斷。其典型模式為:

                                  ....

                                  關中斷;

                                  臨界區;

                                  開中斷;

                                  .....

                          這種方法限制了處理機交替執行的能力,因此執行的效率將會明顯降低。對核心來說,當它執行更新變數或列表的幾條指令期間關中斷是很方便的,但是

                     將關中斷的權利交給使用者則很不明智,若一個程序關中斷之後不再開中斷,則系統可能會因此終止。

  (2)、硬體指令方法:設立原子操作指令

                                TestAndSet 指令:這條指令是原子操作,即執行該程式碼時不允許被中斷。其功能是:讀出指定標誌後把該標誌設定為真。指令功能的描述如下:

                                 boolean TestAndSet(boolean *lock){

                                                boolean old;

                                                old = *lock;

                                                *lock = true;

                                                return old; 

                                 }

                              可以為每個臨界資源設定一個共享布林變數  lock ,表示資源的兩種狀態:true 表示正被佔用,初值為  false 。在程序訪問臨界資源之前,利用

                        TestAndSet 檢查和修改標誌 lock ;若有程序在臨界區,則重複檢查,直到程序退出。利用該指令實現程序互斥的演算法描述如下:

                               while TestAndSet(&lock);

                               程序的臨界區程式碼段;

                               lock =  false;

                               程序的其他程式碼;

                              Swap  指令該指令的功能是交換兩個字(位元組)的內容。其功能描述如下:

                                Swap(boolean *a , boolean *b){

                                        boolean temp;

                                        temp = *a;

                                        *a = *b;

                                        *b = temp;

                                }

                               應為每個臨界資源設定一個共享布林變數 lock ,初值為   false;在每個程序中再設定一個區域性布林變數 key ,用於與 lock 變換資訊。在進入臨界區之前

                          先利用 Swap 指令交換 lock 與 key 的內容,然後檢查 key 的狀態;有程序在臨界區時,重複交換和檢查過程,直到程序退出。利用Swap 指令實現程序互斥的

                         演算法描述如下:

                               key = true;

                               while(key != false);

                               Swap(&lock , &key);

                               程序的臨界區程式碼段;

                               lock = false;

                               程序的其他程式碼;

                              硬體方法的優點:適用於任意數目的程序,不管是單處理機還是多處理機;簡單、容易驗證其正確性。可以支援程序內有多個臨界區,只需為每個臨界區

                         設立一個布林變數。

                              硬體方法的缺點:程序等待進入臨界區時要耗費處理時間,不能實現讓權等待。從等待程序中隨機選擇一個進入臨界區,有程序可能一直選不上,從而

                          導致“飢餓”現象。

                              注意:以上對  TestAndSet  和 Swap 指令的描述僅僅是功能實現,並非軟體實現定義,事實上它們是由硬體邏輯直接實現的,不會被中斷。

                              補充:以上的程式碼實現與我們平時在編譯器上寫的程式碼意義不同,以上的程式碼實現是為了表述程序實現同步和互斥的過程,並不是說計算機內部實現

                                           同步互斥就是這些程式碼。

----------訊號量:利用PV 操作實現互斥