操作系統6--死鎖

6.1 死鎖的引入

在之前我們或多或少都涉及到了死鎖,最直接的例子就是哲學家就餐筷子,如果每一個哲學家都拿起了他的右手的筷子,現在都在等左邊的筷子。這樣一直繞下去,從而產生了死鎖。

6.1.1 資源問題

在系統中存在着很多不同類型的資源,其中可以引起的死鎖的主要是需要採用互斥訪問方法的、不可以被搶佔的資源、

6.1.1.1 可重用性資源和消耗性資源

可重用性資源

可重用性資源是一種可供用戶重複使用多次的資源,它具有以下性質:

  1. 每一個可重用性資源中的單位只能分配給一個進程使用,不允許多個進程共享
  2. 進程在使用可重用性資源時,須按照這樣的順序:請求資源 -> 使用資源 -> 釋放資源
  3. 系統中每一類可重用性資源中的單元數目是相對固定的,進程在運行期間既不能創建也不能刪除它

可消耗性資源

可消耗性資源又稱爲臨時性資源,它是在進程運行期間,由進程動態的創建和消耗的,它具有以下性質:

  1. 每一類可消耗性資源的單元數目在進程運行期間是可以不斷變化的,有時它可以有很多,有時可能爲0
  2. 進程在運行過程中,可以不斷地創造可消耗性資源的單元,將它們放入該資源類的緩衝區中,以增加該資源類的單元數目
  3. 進程在運行過程中,可以請求若干個可消耗性資源單元,用於進程自己的消耗,不再將它們返回給該資源類中

6.1.1.2 可搶佔性資源和不可搶佔性資源

可搶佔性資源

可把系統中的資源分爲兩類,一類是可搶佔性資源,是指某進程在獲得這類資源後,該資源可以再被其它進程或系統搶佔

不可搶佔性資源

另一類資源是不可搶佔資源,一旦系統把某資源分配給該進程之後,就不能將它強行回收,只能在進程用完後自行釋放

6.1.2 死鎖的起因

6.1.2.1 競爭不可搶佔性資源引起的死鎖

通常系統中所擁有的不可搶佔性資源其數量不足以滿足多個進程運行的需要,使得進程在運行過程中,會因爭奪資源而陷入僵局。

一個很簡單的例子,進程P1P_1P2P_2在併發執行,他們都要寫兩個文件F1F_1F2F_2。其中P1P_1P2P_2的代碼分別爲:

   P1
  ....
Open(f1, w);
Open(f2, w);

   P2
  ....
Open(f2, w);
Open(f1, w);

如果這兩個進程在併發執行的時候,如果P1P_1先打開F1F_1F2F_2,然後P2P_2纔去打開F1F_1(或F2F_2),由於文件F1F_1(或F2F_2)已經被打開,因此P2P_2會被阻塞。當P1P_1使用完F1F_1(或F2F_2),這時P2P_2纔可以去打開F1F_1(或F2F_2),這樣程序繼續運行下去。

但是如果在P1P_1打開F1F_1的同時,P2P_2去打開F2F_2,每個進程都佔有一個打開的文件,此時就可能出現問題。因爲當P1P_1試圖去打開F2F_2,而F2F_2試圖去打開F1F_1時,這兩個進程都會因文件已被打開而阻塞,因此這兩個進程將會無限期地等待下去,從而形成死鎖。
共享文件時的死鎖

6.1.2.2 競爭可消耗資源引起的死鎖

進程之間通信時的死鎖
如圖所示,m1m_1m3m_3m3m_3是可消耗資源。進程P1P_1一方面產生消息m1m_1,利用send(p2,m1)將它發送給P2P_2,另一方面,有要求從P3P_3接受消息m2m_2;而P2P_2P3P_3依次類推。

如果三個進程按以下順序進行:

P1:   ...send(p2, m1);    receive(p3, m3);...
P2:   ...send(p3, m2);    receive(p1, m1);...
P3:   ...send(p1, m3);    receive(p2, m2);...

這三個進程都可以先將消息發送給下一個進程,相應地他們也都能都接收到從上一個進程發來的消息,因此三個進程都可以順利的進行下去,不會發生死鎖。

但是如果三個進程都先執行receive,在執行send,按下面的順序運行:

P1:   ...receive(p3, m3);    send(p2, m1);...
P2:   ...receive(p1, m1);    send(p3, m2);...
P3:   ...receive(p2, m2);    send(p1, m3);...

那麼這三個進程就會永遠阻塞在它們的receive操作上,就會產生死鎖。

6.2 死鎖的定義、必要條件和處理方法

6.2.1 死鎖的定義

如果一組進程中的每一個進程都在等待僅由該組進程中的其它進程才能引發的事件,那麼該組進程是死鎖的。

6.2.2 產生死鎖的必要條件

產生死鎖必須同時具備下面四個必要條件,只要其中任一個條件不成立,死鎖就不會發生:

  1. 互斥條件。進程對所分配的資源進行排他性使用。即該資源只允許一個進程使用,其他進程如果請求該資源只能等待。
  2. 請求和保持條件。進程已經保持一至少一個資源,但又提出了新的資源請求,而新資源已被其他進程佔有,導致進程被阻塞。
  3. 不可搶佔條件。 進程已獲得的資源在爲使用完之前不能被搶佔,只有進程在使用完之後才能釋放。
  4. 循環等待條件。發生死鎖時,必然存在一個進程資源循環鏈,即進程集合[P0,P1,P2,,Pn][P_0, P_1, P_2, ···, P_n]P0P_0正在等待一個P1P_1佔用的資源,P1P_1正在等待一個P2P_2佔用的資源,……,PnP_n正在等待一個P0P_0佔用的資源

6.2.3 處理死鎖的辦法

目前處理死鎖的方法可歸結爲四種:

  1. 預防死鎖。通過設置某些限制,去破壞產生死鎖四個必要條件中的一個或幾個來預防死鎖。
  2. 避免死鎖。在資源的動態分配過程中,用某種方法阻止系統進入不安全狀態,從而避免發生死鎖。
  3. 檢測死鎖。該方法允許進程在運行過程中發生死鎖,但可通過檢測機構及時地檢測出死鎖的發生,然後採取適當措施,把進城從死鎖中解脫出來。
  4. 解除死鎖。當檢測到系統中已發生死鎖時,就採取相應措施,將進程從死鎖狀態中解脫出來。

6.3 預防死鎖

預防死鎖時通過破壞產生死鎖四個必要條件中的一個或幾個,以避免發生死鎖。

6.3.1 破壞“請求和保持”條件

當一個進程在請求資源時,他不能持有不可搶佔資源。可通過一下兩種不同的協議實現:

6.3.1.1 第一種協議

所有進程在開始運行之前,必須一次性地申請其在整個運行過程中所需的全部資源。此時若系統中有足夠的資源分配給某進程,便可把其需要的所有資源分配給它。這樣,該進程在整個運行期間,便不會再提出資源要求,從而破壞了“請求”條件。系統在分配資源時,只要有一種資源不能滿足進程的要求,即使其所需的其他資源都空閒也不分配給它,而讓該進程等待。由於該進程在等待期間未佔有任何資源,於是破壞了“保持”條件,從而可以預防死鎖的發生。

這種協議的優點是簡單、易行且安全。但是缺點也極其明顯:

  1. 資源被嚴重浪費,嚴重的惡化了資源的利用率。
  2. 使進程經常的發生飢餓現象。

6.3.1.2 第二種協議

該協議是對第一種協議的改進,它允許一個進程只獲得運行初期所需的資源後,便開始運行。進程運行的過程中再逐步釋放已分配給自己的、且已用畢的全部資源,然後再請求新的所需資源。

6.3.2 破壞“不可搶佔”條件

爲了能破壞“不可搶佔”條件,協議中規定,當一個已經保持了某些不可被搶佔資源的進程,提出新的資源請求而不能得到滿足時,他必須釋放已經保持的所有資源,待以後需要時再重新申請。這意味着進程已佔有的資源會被暫時地釋放,或者說是被搶佔了,從而破壞了“不可搶佔”條件。

6.3.3 破壞“循環等待”條件

一個能保證“循環等待”條件不成立的方法是,對系統所有資源類型進行線性排序,並賦予不同的序號。排序後,便可以採用這樣的預防協議:規定每個進程必須按照序號的地址順序來請求資源。一個進程在開始時,可以請求資源RiR_i的單元,以後,當且僅當F(Rj)>F(Ri)F(R_j)>F(R_i),進程纔可以請求資源RjR_j。如果需要多個同類資源單元,則必須一起請求。

  • 優點:資源利用率和系統吞吐量都有比較明顯的改善。
  • 缺點:
    1. 系統中各類資源所規定的序號必須穩定,這就限制了新類型設備的增加
    2. 可能會發生作業使用各類資源的順序與系統規定的不同,造成資源的浪費。
    3. 這種按照規定次序申請資源的方法會限制用戶簡單,自主的編程。

6.4 避免死鎖

6.4.1 系統安全狀態

在死鎖避免方法中,把系統的狀態分爲安全狀態和不安全狀態。當系統處於安全狀態時,可避免發生死鎖。反之,當系統處於不安全狀態時,則可能進入到死鎖狀態。

在該方法中,允許進程動態的申請資源嗎,但系統在進行資源分配之前,應先計算此次資源分配的安全性。若此次分配不會導致系統進入不安全狀態,纔可將資源分配給進程,否則,另進程等待。

安全狀態是指系統能按某種進程推進順序爲每個進程分配其所需資源,直至滿足每個進程對資源的最大需求。如果無法找到這樣一個序列,則稱系統處於不安全狀態。

6.4.2 利用銀行家算法避免死鎖

爲實現銀行家算法,每一個新進程在進入系統時,他必須申明在運行過程中,可能需要每種資源類型的最大單元數目,其數目不應超過系統所擁有的資源總量。當進程請求一組資源時,系統必須首先確定是否有足夠的資源分配給進程。若有,再進一步計算在將這些資源分配給進程後,是否會使系統處於不安全狀態。如果不會,才分配資源。

6.4.2.1 銀行家算法中的數據結構

  1. 可利用資源向量Available。代表系統中目前已有的可分配的該種資源的最大數。
  2. 最大需求矩陣Max。代表該進程對該資源的最大需求數。
  3. 分配矩陣Allocation。代表目前已經分配給該進程的該資源的數目。
  4. 需求矩陣Need。代表該進程還需要該資源的數目。

6.4.2.2 銀行家算法

RequestiRequest_i是進程PiP_i的請求向量,如果Requesti[j]=KRequest_i[j]=K,表示進程PiP_i需要KKRjR_j類型的資源。當PiP_i發出資源請求後,系統按下述步驟進行檢查:

  1. 如果Requesti[j]Need[i,j]Request_i[j] \le Need[i, j],便轉向步驟2;否則任務出錯,因爲它所需要的資源數已超過它所宣佈的最大值。
  2. 如果Requesti[j]Available[j]Request_i[j] \le Available[j],便轉向步驟3;否則,表示尚無足夠資源,PiP_i需等待。
  3. 系統試探着把資源分配給進程PiP_i,並修改下面數據結構中的數值:
    Available[j]=Available[j]Requesti[j]Available[j] = Available[j] - Request_i[j]
    Allocation[i,j]=Allocation[i,j]+Requesti[j]Allocation[i,j] = Allocation[i,j] + Request_i[j]
    Need[i,j]=Need[i,j]Requesti[j]Need[i,j] = Need[i,j] - Request_i[j]
  4. 系統執行安全性算法,檢查此次資源分配後系統是否處於安全狀態。若安全,才正式將資源分配給進程PiP_i,已完成本次分配;否則,將本次的試探分配作廢,恢復原來的資源分配狀態,讓進程PiP_i等待。

6.4.2.3 安全性算法

  1. 設置兩個向量:①工作向量Work,他表示系統可提供給進程繼續運行所需的各類資源數目,它含有m個元素,在執行安全算法開始時,Word=AvailableWord = Available;②FInish:他表示系統是否有足夠的資源分配給進程,使之運行完成。開始時先做Finish[i]=falseFinish[i] = false;當有足夠資源分配給進程時,再令Finish[i]=trueFinish[i] = true
  2. 從進程集合中找到一個能滿足下述條件的進程:①Finish[i]=falseFinish[i]=false;②Need[i,j]Work[j]Need[i,j] \le Work[j];若找到則執行步驟3,否則執行步驟4.
  3. 若進程PiP_i獲得資源後,可順利執行,直至完成,並釋放出分配給他的資源,故應執行:
    Word[j]=Work[j]+Allocation[i,j];Word[j] = Work[j] + Allocation[i,j];
    Finish[i]=true;Finish[i]=true;
    go to step 2;
  4. 如果所有進程的Finish[i]=trueFinish[i]=true都滿足,則表示系統處於安全狀態;否則,系統處於不安全狀態。

6.4.2.4 銀行家算法例子

題目

假定系統中有五個進程{P0,P1,P2,P3,P4}\{P_0, P_1, P_2, P_3, P_4\}和三類資源{A,B,C}\{A, B, C\},各種資源的數量分別爲10、5、7,在T0T_0時刻的資源分配情況如圖所示:
T0時刻的資源分配表

T0T_0時刻的安全性

利用安全性算法對T0T_0時刻的資源分配情況進行分析可知,在T0T_0時刻存在着一個安全序列{P1,P3,P4,P2,P0}\{P_1,P_3,P_4,P_2,P_0\},故系統是安全的。
T0時刻的安全序列

P1P_1請求資源

P1P_1發出請求向量Request1(1,0,2)Request_1(1,0,2),系統按銀行家算法進行檢查:
Request1(1,0,2)Need1(1,2,2)Request_1(1,0,2) \le Need_1(1,2,2)
Request1(1,0,2)Available1(3,3,2)Request_1(1,0,2) \le Available_1(3,3,2)
③系統先假定可爲P1P_1分配資源,並修改AvaliableAvaliableAllocation1Allocation_1Need1Need_1向量,由此形成的資源變化情況如1圖中的圓括號所示;
④再利用安全性算法檢查此時系統是否安全,如圖所示
P1申請資源時的安全性檢查

有所進行的安全性檢查得知,可以找到一個安全序列{P1,P3,P4,P2,P0}\{P_1,P_3,P_4,P_2,P_0\}。因此,系統是安全的,可以立即將P1P_1所申請的資源分配給它。

P4P_4請求資源

P4P_4發出請求向量Request4(3,3,0)Request_4(3,3,0),系統按銀行家算法進行檢查:
Request4(3,3,0)Need4(4,3,1)Request_4(3,3,0) \le Need_4(4,3,1)
Request4(3,3,0)Available(2,3,0)Request_4(3,3,0) \ge Available(2,3,0),讓P4P_4等待。

P0P_0請求資源

P0P_0發出請求向量Request0(0,2,0)Request_0(0,2,0),系統按銀行家算法進行檢查:
Request0(0,2,0)Need0(7,4,3)Request_0(0,2,0) \le Need_0(7,4,3)
Request0(0,2,0)Available(2,3,0)Request_0(0,2,0) \le Available(2,3,0)
③系統暫時先假定可爲P0P_0分配資源,並修改有關數據,如圖所示。
爲P0分配資源後的有關資源數據

進入安全性檢查

可用資源Available(2,1,0)Available(2,1,0)已不能滿足任何進程的需要,故系統進入不安全狀態,此時系統不分配資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章