1. 程式人生 > >作業系統之程序—臨界區管理 (二)

作業系統之程序—臨界區管理 (二)

1.臨界區管理

 臨界區:併發程序中與共享變數有關的程式段

  •  臨界資源:共享變數代表的資源

2.臨界區解決互斥問題

如果能保證程序在臨界區執行時,不讓另一個程序進入臨界區,即各程序對共享變數的訪問是互斥的,就不會造成與時間有關的錯誤

3.臨界區的排程原則

  • 一次至多允許一個程序進入臨界區內

  • 如果已有程序在臨界區中,試圖進入此臨界區的其他程序應等待;
  • 進入臨界區的程序應在有限時間內退出,以便讓等待佇列中的一個程序進入

臨界區排程原則可總結成四句話:互斥使用、有空讓進;忙則等待、有限等待;擇一而入、讓權等待;演算法可行  演算法可行是指不能因為所選的排程策略造成程序飢餓甚至死鎖

4.實現臨界區管理的軟體方法

(1)Dekker演算法

Dekker演算法用一個指示器turn來指示應該哪一個程序進入臨界區。

  • 若turn=0則程序P1可以進入臨界區
  • 若turn=1則程序P2可以進入臨界區

演算法實現

前提條件:共享變數有turn和flag[],turn==0&&flag[1]==ture則輪到po執行臨界區程式碼;turn==1&&flag[1]==ture則輪到p1執行臨界區程式碼

  1. 首先要執行的執行緒將自己對應的flag置為true,ture代表請求訪問(小學生舉手提問)
  2. 接著判斷其他縣城是否也將自己的flag也置為ture(判斷其他小學生是不是也舉手了)
  3. 如果其他執行緒的flag為false開始判斷trun的值,如果turn的值為1也就是說輪到了其他執行緒,則將當前執行緒的flag置為false,並開始迴圈等待,等待條件就是監測turn的值,當turn的值改變的時候,將當前縣城的flag置為true,開始臨界區訪問,訪問結束將turn值改變並將flag置為false(如果有其他小學生舉手了,就要判斷是不是現在輪到其他小學生,如果是輪到其他小學生則讓當前小學生把手放下並且等其他小學生提問完,當提問完後再把舉手,之後提問問題,問題提問完後把話語權給其他小學生並且當前小學生把手放下)
1)P0的邏輯

while(true){
    flag[0] = true;// 首先P0舉手示意我要訪問
    while(flag[1]) {// 判斷P1是否也舉手了
        if(turn==1){// 如果P1也舉手了,那麼就看看到底輪到誰
            flag[0]=false;// 如果確實輪到P1,那麼P0先把手放下(讓P1先)
            while(turn==1);// 只要還是P1的時間,P0就不舉手,一直等
            flag[0]=true;// 等到P1用完了(輪到P0了),P0再舉手
        }
        flag[1] = false; // 只要可以跳出迴圈,說明P1用完了,應該跳出最外圈的while
    }
    visit();// 訪問臨界區
    turn = 1;// P0訪問完了,把輪次交給P1,讓P1可以訪問
    flag[0]=false;// P0放下手
}
2)P1的邏輯
while(true){
    flag[1] = true;// 先P1舉手示意我要訪問
    while(flag[0]) {// 如果P0是否也舉手了
        if(turn==0){// 如果P0也舉手了,那麼久看看到底輪到誰
            flag[1]=false;// 如果確實輪到P0,那麼P1先把手放下(讓P0先)
            while(turn==0);// 只要還是P0的時間,P1就不舉手,一直等
            flag[0]=true;// 等到P0用完了(輪到P1了),P1再舉手
        }
        flag[0] = false; // 只要可以跳出迴圈,說明P0用完了,應該跳出最外圈的while
    }
visit();// 訪問臨界區
turn = 0;// P1訪問完了,把輪次交給P0,讓P0可以訪問
flag[1]=false;// P1放下手

(2)Peterson演算法

Peterson演算法是一個實現互斥鎖的併發程式設計演算法,可以控制兩個執行緒訪問一個共享的單使用者資源而不發生訪問衝突。Gary L. Peterson於1981年提出此演算法

此演算法需要將步驟首先需要隊turn賦值

void procedure0(){
    while(true){
        flag[0]=true;
        while(flag[1]&&turn==1);/*若flag[1]為false,P0就進入臨界區;若flag[1]為tureP0循
                                    環等待,只要P1退出臨界區,P0即可進入*/
        visit();/*訪問臨界區*/
        flag[0]=false;/*訪問臨界區完成,procedure0釋放出臨界區*/
        turn=1;
        /*remainder section*/
    }
}
void procedure1(){
    while(true){
        flag[1]=true;
        while(flag[0]&&turn==0);/*若flag[1]為false,P1就進入臨界區;若flag[0]為tureP0循
                                    環等待,只要P0退出臨界區,P1即可進入*/
        visit();/*訪問臨界區*/
        flag[1]=false;/*訪問臨界區完成,procedure1釋放出臨界區*/
        turn=0;
        /*remainder section*/
    }
}

(3)測試只用一個標誌變數 

發現也可以實現臨界區管理

    @Override
    public void run() {
        while(true){
            while(turn==ids){
                ss=(ss+1)%12000;
                if(ss==0) System.out.println("餓死了");
            }
            pp();
            turn=ids;
        }
    }

    public void pp(){
        number+=1;
        number+=1;
        System.out.println("this number id :"+id+"is number:"+number);
        number-=1;
        number-=1;
    }

5.實現臨界區管理的硬體設施

  • 由於程序切換需要依賴中斷來實現,如果遮蔽中斷,則不會引起程序切換
  • 因此為了實現對臨界資源的互斥使用,可以在程序進入臨界區之前關閉中斷,等程序退出臨界區以後在開啟中斷
  • 中斷被遮蔽後,處理器不響應中斷,則不會被切換
  • 於是一旦遮蔽中斷,程序就可以進入臨界區修改訪問共享變數,而不用擔心其他程序介入

(1)關中斷 

  • 關中斷是實現互斥的最簡單方法之一
  • 程序在測試標誌之前,首先關中斷,直到測試完並設定標誌之後才開中斷
  • 程序在臨界區執行期間,計算機系統不響應中斷
  • 因此,不會轉向排程,也就不會引起程序或執行緒切換,正在執行標誌測試和設定的程序或執行緒不會被打斷,從而保證了互斥

關中斷方法的缺點

  • 關中斷時間過長會影響系統效率,限制處理器交叉執行程式的能力
  • 關中斷方法也不適用於多CPU系統,因為,在一個處理器上關中斷,並不能防止程序在其他處理器上執行相同臨界區程式碼

硬體設施缺點

  • 忙等現象仍然存在,但由於使用機器指令測試,則忙等時間可以忍受 (忙等的含義是當一個程序位於其臨界區內時,任何試圖進入其臨界區的程序都必須在其進入程式碼中連續地迴圈)
  • 導致飢餓:臨界區空閒時,執行迴圈檢測的若干程序能進入臨界區的機率是相等的,有的程序可能運氣很不好,很難有機會進入臨界區,因而飢餓
  • 導致死鎖:程序P1和P2,P1的優先順序低於P2,P1通過測試進入臨界區,這時P2到, P2搶佔了P1的處理器開始執行, P2也要求進入臨界區,但臨界區被P1佔,而P1的優先順序沒有P2高不可能搶佔P2的處理器。這樣,這兩個程序互相等待, P1等P2釋放處理器, P2一直忙等P1釋放臨界區,如果沒有外力,這兩個程序處於死鎖
  • 不會成為一種通用的方法

解決方案

  • 軟體方法和硬體方法都存在忙等問題,浪費了處理器的時間
  • 訊號量方法可以實現程序同步與互斥,而不需要忙等

6.總結

通過臨界區的學習,我們可以知道主要是通過標誌變數決定臨界區進入的決定權,當一個執行緒進入臨界區的時候,其他執行緒通過迴圈等待也就是忙等來等待進入臨界區。由於忙等的問題會浪費處理器時間,所以接下來我們學習下一章節訊號量來解決忙等這一缺陷的問題。