一、死鎖概念及其資源分配圖
過橋例子
橋只能一個方向通行;
橋的每一個部分都可以看成資源;
如果死鎖發生,可能很多車都無法通行;
解決方法:可由一輛車返回解決(搶佔資源並回退);
有可能產生飢餓。
死鎖概念:
在多道程序環境下,一組處於等待狀態的進程,其中每一個進程都持有資源。並且等待着由這組中其他進程所持有的資源,那麼該組等待進程有可能再也無法改變其狀態,這種情況稱爲死鎖。
所有的死鎖進程如果沒有外力的介入,都無法向前推進。
引起死鎖的主要原因:
(1)競爭互斥資源;
(2)進程推進不當。
例如,系統有兩個磁帶設備,進程P1和P2各佔有一個磁帶設備,並且實際需要兩個磁帶設備,此時P1和P2均死鎖。
兩個磁帶設備是互斥資源。
死鎖的特徵(必要條件)
- 互斥:一次只有一個進程可以使用一個資源
- 佔有並等待:一個至少持有一個資源的進程等待獲得額外的由其他進程所持有的資源
- 不可搶佔:一個資源只有當持有它的進程完成任務後,自由的釋放
- 循環等待:等待資源的進程之間存在環{P0,P1,.…,P0}
P0等待P1佔有的資源,P1等待P2佔有的資源,…,Pn-1等待Pn佔有的資源,Pn等待P0佔有的資源。
四個條件同時出現,死鎖將會發生。
系統模型
爲了研究死鎖,建立系統模型。
資源類型的例子:
內存空間、CPU週期、文件、I/O設備等。
形式化表示爲:資源類型R1,R2,…, Rm
進程使用資源順序:
- 申請資源
分爲動態申請資源和靜態申請資源;
動態申請資源,即在進程運行中申請資源;
靜態申請資源,即在進程運行前一次性申請所有資源。 - 使用資源
- 釋放資源
資源分配圖
一個頂點的集合V和邊的集合E
- V被分爲兩個部分
P={P1,P2…,Pn},含有系統中全部的活動進程
R={R1,R2,…,Rm},含有系統中全部的資源 - 請求邊:由進程Pi到資源Rj的有向邊記爲Pi→Rj,表示進程Pi申請了資源Rj的一個實例,該邊稱爲申請邊;
- 分配邊:由資源Rj到進程Pi的有向邊記爲Rj→Pi,表示資源Rj的一個實例已經分配給選程Pi,該邊稱爲分配邊。
例子:
如果分配圖沒有環,那麼系統就沒有進程死鎖;
如果有環,那麼可能存在死鎖。
有環有死鎖的資源分配圖
系統中有最小的兩個環, 此時進程P1,P2,P3死鎖。
有環但沒有死鎖的資源分配圖
因爲進程P4可能釋放R2的實例,這個資源可分配給P3,打破了環。
結論:
- 如果圖沒有環,那麼不會有死鎖
- 如果圖有環,
- 如果每一種資源類型只有一個實例,那麼死鎖發生
- 如果一種資源類型有多個實例,可能死鎖
處理死鎖的方法
- 可使用協議來預防或避免死鎖,確保系統不會進入死鎖狀態。
系統可以採用死鎖預防或死鎖避免方案。 - 可允許系統進入死鎖狀態,然後檢測它,並加以恢復。
系統可提供死鎖檢測算法,並提供死鎖灰復算法從死鎖中恢復。 - 可忽略這個問題,認爲死鎖不可能在系統內發生。
此方法爲絕大多數操作系統所採用,包括Unix、Windows,因此需要由應用程序的開發人員自己來處理死鎖。
二、死鎖預防
死鎖預防(deadlock prevention)是一組方法。只要確保至少一個必要條件不成立,抑制死鎖發生的必要條件(互斥、佔有並等待、非搶佔、循環等待),就能預防死鎖。
死鎖的預防1
- 非共享資源(互斥資源),必須要有互斥條件;
- 共享資源,不涉及死鎖;
- 現代操作系統中的虛化技術,將互斥資源改造爲共享資源;
- 互斥:如系統存在互斥資源,不能改變這個條件來預防死鎖。
死鎖的預防2
佔有並等待:必須保證進程申請資源的時候沒有佔有其他資源
靜態分配策略;
一種可用的協議是每個進程在執行前一次性申請並獲得所有資源。
另外一種協議則允許進程在沒有資源時纔可申請資源。
按照第一種協議,進程一開始就要申請DVD驅動器、磁盤、打印機,在其執行期間他始終佔有打印機。
第二種協議允許進程開始時只申請DVD驅動器、磁盤,複製完數據後,釋放這兩個設備,然後再申請磁盤和打印機,當數據打印完後,再釋放資源並終止。
缺點:
-
資源利用率低
許多資源可能已分配,但很長時間沒有被使用。 -
可能發生飢餓
一個進程如果需要多個常用資源,可能會永久等待,因爲其所需要的資源中,至少有一個已分配給其他資源。
死鎖的預防3
非搶佔:
如果一個進程的申請沒有實現,它要釋放所有佔有的資源。
先佔的資源放入進程等待資源列表中;
進程在重新得到舊的資源的時候可以重新開始。
死鎖的預防4
循環等待:
對所有的資源類型排序進行總排序,並且要求進程按照遞增順序申請資源。
設R={R1,R2,…, Rm)爲資源類型的集合,爲每個資源類型分配一個唯一整數來允許比較兩個資源以確定其順序。
爲預防死鎖,每個進程只按遞增順序申請資源,即一個進程開始可以申請任何資源類型Ri的實例。
當且僅當Rj的值大於Ri的值時,進程纔可以申請Rj的實例。
一個進程如果同時需要使用磁帶驅動器和打印機,那就必須先申請磁帶驅動器,再申請打印機。
設計一個完全排序方法並不能防止死鎖,而要靠應用程序員來按照順序編寫程序。
各資源類型的序號確定也是至關重要的,一般要按照系統內部資源使用的正常順序定義。
三、死鎖避免
死鎖避免
死鎖預防算法
操作:通過限制資源申請的方法來預防死鎖;
副作用:降低了設備使用率和系統吞吐量。
避免死鎖的另一個方法:
獲得以後如何申請資源的附加信息來決定是否分配資源。
每次申請要求系統考慮現有可用資源、現已分配給每個進程的資源和每個進程將來申請與釋放的資源,以決定申請是否滿足,從而避免死鎖發生的可能性。
最爲簡單和最爲有用的模型要求進程說明可能需要的每種資源類型實例的最大需求。
需要系統有一些額外的信息:
- 一個簡單而有效的模型要求每一個進程聲明它所需要的資源的最大數
- 死鎖避免算法動態檢查資源分配狀態以確保循環等待條件不可能成立
- 資源分配狀態定義爲可用的與已分配的資源數,和進程所需的最大資源量所決定
這裏的資源分配狀態是由可用資源和已分配資源、及進程最大需求所決定的。
安全狀態
當進程申請一個有效的資源的時候,系統必須確定分配後是安全的。
如果存在一個安全序列,系統處於安全態。
進程序列<P1,P2,…,Pn>是安全的,如果每一個進程Pi所申請的可以被滿足的資源數加上其他進程所持有的該資源數小於系統總數。
- 如果Pi需要的資源不能馬上獲得,那麼Pi等待直到所有的Pi-1進程結束;
- 當Pi-1結束後,Pi獲得所需的資源,執行、返回資源、結束;
- 當Pi結束後,Pi+1獲得所需的資源執行,依此類推。
安全狀態與死鎖狀態的關係
安全狀態不是死鎖狀態,相反,死鎖狀態是不安全狀態。
- 如果一個系統在安全狀態,就沒有死鎖;
- 如果一個系統不是處於安全狀態,就有可能死鎖;
- 死鎖避免→確保系統永遠不會進入不安全狀態
採用這種方案,如果進程申請一個現已可用的資源,它有可能必須等待。因此,與沒有采用死鎖避免算法相比,這種情況下資源利用率可能更低。
避免算法
- 如果有一個系統,每種資源類型只有一個實例,
單實例資源
資源分配圖法:
在原資源分配圖中,除了申請邊和分配邊外,引入一種新類型的邊,稱爲需求邊。
- 需求邊Pi→Rj: Pi可能以後需要申請Rj資源,用虛線表示;
- Pi申請Rj資源,需求邊轉換爲請求邊;
- 請求邊在資源分配後轉換爲分配邊;
- 資源釋放後,分配邊轉換爲需求邊。
假設Pi申請資源Rj,
請求能滿足的前提是:把請求邊轉換爲分配邊後不會導致環存在
如果P2申請資源R2,雖然R2可用,但是不能分配資源給P2,因爲這樣會創建一個環,環表示系統處於不安全狀態。
從算法效率上講,環檢測算法需要n2級的操作。其中n是圖中節點數,也就是系統中進程數量。
- 對於每種資源類型有多個實例的系統,
多實例資源
銀行家算法(效率比資源分配圖法要低)
- 多個實例;
- 每一個進程必須事先聲明使用的最大量;
- 當一個進程請求資源,它可能要等待;
- 當一個進程得到所有的資源,它必須在有限的時間釋放它們。
爲了實現銀行家算法,必須有幾個數據結構,這些結構對系統的狀態進行了記錄,其中n爲系統進程的個數,m爲資源類型的種類。
銀行家算法的數據結構:
- Available:長度爲m的向量。如果available[j]=k,那麼資源Rj有k個實例有效;
- Max:n*m矩陣。如果Max[i,j]=k,那麼進程Pi最多可以請求k個資源Rj的實例;
- Allocation:n*m矩陣。如果Allocation[i,j]=k,那麼進程Pi當前分配了k個資源Rj的實例;
- Need:n*m矩陣。如果Need[i,j]=k,那麼進程Pi還需要k個資源Rj的實例;
Need[i,j] = Max[i,j] - Allocation[i,j]
安全算法:
- 讓Work和Finish作爲長度爲m和n的向量初始化:
Work:=Available
Finish[i]=false for i-1,3,……,n. - 查找i
(a)Finish[i]=false
(b)Needi ≤ Work
If no such i exists,go to step 4. - Work:=Work+Allocation,
Finish[i]:=true
go to step 2. - 如果對所有i的Finish[i]=true,則系統處在安全狀態。
是否滿足進程Pi的資源請求算法:
Requesti=進程Pi的資源請求向量。如果Requesti[m]=k,則進程Pi想要資源類型爲Rjm的k個實例
- 如果Requesti ≤ Needi,轉step2. 否則報錯,因爲進程請求超出了其聲明的最大值;
2.如果Requesti ≤ Available,轉step3. 否則Pi必須等待,因爲資源不可用. - 假設通過修改下列狀態來分配請求的資源給進程Pi:
Available:=Avalilable-Requesti;Alocationi:=Alocationi+Requesti;Needi:=Needi-Requesti;
如果系統安全→將資源分配給Pi;
如果系統不安全→Pi必須等待,恢復原有的資源分配狀態。
銀行家算法的例子:
現在假設P1請求一個資源類型A和兩個資源類型C,即Request1=(1,0,2)
四、死鎖檢測和解除
允許系統進入死鎖狀態,也就是說系統既不採用死鎖預防算法也不採用死鎖避免算法,然後檢測死鎖並恢復系統。
系統應該提供:一個用來檢測系統是否出現了死鎖的算法和一個用來從死鎖狀態中恢復的算法。
每一種資源類型只有一個實例
- 維護等待圖:
節點是進程,
Pi→Pj表明Pi在等待Pj - 定期調用算法來檢查是否有環
- 一個檢查圖中是否有環的算法需要n2的操作來進行,n爲圖中的節點數
一個資源類型的多個實例
- Available:一個長度爲m的向量,表示每一種資源類型可用的實例數目
- Allocation:一個n*m的矩陣,定義了當前分配的每一種資源類型的實例數目
- Request:一個n*m的矩陣,表明了當前的進程請求。如果Request[i,j]=k,那麼進程Pi請求k個資源Rj的實例
檢測算法的具體步驟
- 讓Work和Finish作爲長度爲m和n的向量初始化
(a)Work=Available
(b)For i=0,2.…,n-1,if Allocation ≠ 0,thenFinish[i]=false;otherwise,Finish[i]=true. - 找到滿足下列條件的下標i
(a)Finish[i]=false
(b)Requesti ≤ Work
如果沒有這樣的i存在,轉4 - Work=Work +Allocationi;
Finish[i]=true
轉2. - 如果有一些i(0 ≤ i ≤ n),Finish[i] = false,則系統處在死鎖狀態。而且,如果Finish[i]=false,則進程Pi是死鎖的。
算法需要m*n2次操作來判斷是否系統處於死鎖狀態。
檢測算法的例子
檢測算法的用法
何時及多長時間的調用取決於:
- 死鎖可能發生的頻率
- 有多少進程受影響
每個請求都調用;
因此極端情況下,每次請求不能立即滿足時,就調用死鎖檢測算法。
對於每個請求都調用檢測算法,會引起相當大的開銷。所以可以使用不太高的頻率調用算法,如每小時一次,或當CPU使用率低於40%時。
如果檢測算法被隨意的調用,可能圖中存在很多的環以至於無法判斷是哪一個進程引起了死鎖的發生。
死鎖恢復
- 人工恢復
通知操作員,人工處理 - 自動恢復
(1)終止進程
會收回分配給被終止進程的所有資源。
中斷所有的死鎖進程;
一次中斷一個進程直到死鎖環消失。
影響選擇進程的因素:
進程的優先級;
進程需要計算多長時間,以及需要多長時間結束;
進程使用的資源;
進程完成還需要多少資源;
多少個進程需要被終止;
進程是交互的還是批處理;
(2)搶佔資源
- 選擇一個犧牲品:最小化代價
包含如所擁有的資源數、運行到現在的時間等。 - 回滾:返回到安全的狀態,然後重新開始進程。
- 飢餓:同樣進程的可能總是被選中,在代價因素中加入回滾次數。