【操作系統】死鎖相關知識點

1. 死鎖基本概念

多個併發進程中,如果每個進程持有某種資源而又等待其它進程釋放其他資源,在未改變這種狀態之前都不能向前推進,稱這一組進程產生了死鎖。通俗的講就是 多個進程無限期相互等待 的一種狀態。

2. 死鎖的四個必要條件

死鎖產生的 4 個條件(有一個條件不成立,則不會產生死鎖):

  • 互斥:一個資源一次只能被一個進程使用
  • 請求與保持:一個進程因請求資源而阻塞時,對已獲得資源保持不放
  • 非搶佔:進程獲得的資源,在未完全使用完之前,不能強行搶佔
  • 循環等待:若干進程之間形成一種頭尾相接的環形等待資源關係

搶佔資源和非搶佔資源:

  • 可搶佔資源:可以從擁有它的進程中搶佔而不產生副作用。
  • 不可搶佔資源:不引起相關的計算失敗的情況下,無法把它從佔有它的進程處搶佔過來。

一般來說,可搶佔資源不會引起死鎖,可以在進程間重新分配資源而得到解決。

資源分配圖(RAG):用有向圖描述系統資源和進程的狀態。
在這裏插入圖片描述
進程 P1 已經分得了兩個 R1 資源,並又請求一個 R2 資源;進程 P2 分得了一個 R1 和一個 R2 資源,並又請求一個 R1 資源。

死鎖發生時,系統中一定有由兩個或以上的進程組成的一條環路,該環路中的每個進程都在等待着下一個進程所佔有的資源。用一個有向圖來表示資源分配的情況。用圓形節點表示進程,方形表示資源。從 資源節點到進程節點的有向邊表示該資源被請求、並被進程佔用,從 進程到資源節點的有向邊表示進程正在請求該資源,並且因爲請求資源而導致進程被阻塞,處於等待該資源的狀態。一旦在某個時候有向圖中出現了 兩個或兩個以上進程組成的環路,就會導致死鎖的發生。

死鎖定理:如果資源分配圖中沒有環路,則系統中沒有死鎖,如果圖中存在環路則系統中可能存在死鎖;如果每個資源類中只包含一個資源實例,則環路是死鎖存在的充分必要條件。
在這裏插入圖片描述

3. 死鎖處理方法

死鎖的四個處理方法:

  • 鴕鳥策略:忽略掉死鎖,視而不見
  • 死鎖檢測與恢復:允許死鎖發生,檢測它們是否發生,一旦發生死鎖,就採取行動解決問題
  • 死鎖避免:仔細對資源進行分配,動態地避免死鎖
  • 死鎖預防:破壞引起死鎖的 4 個必要條件

3.1 鴕鳥策略

把頭埋在沙子裏,假裝根本沒發生問題。

因爲解決死鎖問題的代價很高,因此鴕鳥策略這種不採取任務措施的方案會獲得更高的性能。

當發生死鎖時不會對用戶造成多大影響,或發生死鎖的概率很低,就可以採用鴕鳥策略。

大多數操作系統,包括 Unix,Linux 和 Windows,處理死鎖問題的辦法僅僅是忽略它。

3.2 死鎖檢測與恢復

不試圖阻止死鎖,而是當檢測到死鎖發生時,採取措施進行恢復。

死鎖檢測:

  • 每種類型一個資源的死鎖檢測:檢測有向圖中是否存在環。從一個節點出發進行深度優先搜索,對訪問過的節點進行標記,如果訪問了已經標記的節點,就表示有向圖存在環,也就是檢測到了死鎖。
  • 每種類型多個資源的死鎖檢測:每個進程最開始時都不被標記(未執行狀態),執行過程有可能被標記。當算法結束時,任何沒有被標記的進程都是死鎖進程。

三個典型的檢測時機:

  • 當進程由於資源請求不滿足而等待時檢測死鎖,缺點是系統開銷大
  • 定時檢測
  • 系統資源利用率下降時檢測死鎖

每種類型多個資源的死鎖檢測算法:

  1. 尋找一個沒有標記的進程 Pi,要求它所請求的資源小於等於剩餘資源量。
  2. 如果找到了這樣一個進程,就將其佔有的資源量加到剩餘資源量中,並標記該進程,轉回第 1 步。
  3. 如果沒有找到這樣一個進程,說明發生死鎖。

死鎖恢復:

  • 搶佔恢復:死鎖發生的必要條件,其中一個就是不可搶佔。如果允許搶佔,那麼就可以破壞死鎖條件。
  • 回滾恢復:週期性對進程進行檢查點檢查,一旦發現了死鎖,就回滾到一個較早的檢查點上。
  • 殺死進程恢復:殺死一個進程可以釋放它佔有的資源,如果仍然不行那麼久繼續殺死其他進程直到打破死鎖。

3.3 死鎖避免

在程序運行時避免發生死鎖,包括兩種方法:1)資源軌跡圖,2)銀行家算法。

1. 資源軌跡圖

如果知道了進程在各個階段需要哪些資源,那麼可以在圖中進程標註。兩個進程的交疊區域就是一個會造成死鎖的區域。進程在圖中只能向右或者向上前進,一旦進入了危險區,那麼就可能發生死鎖。爲了避免死鎖,應當在合適的時間阻塞某個進程,使得運行避開這個區域。

2. 銀行家算法

銀行家算法的主要思想是避免系統進入不安全狀態。在每次進行資源分配時,它首先檢查系統是否有足夠的資源滿足要求,如果有,則先進行分配,並對分配後的新狀態進行安全性檢查。如果新狀態安全,則正式分配上述資源,否則就拒絕分配上述資源。這樣,它保證系統始終處於安全狀態,從而避免死鎖現象的發生。

安全狀態

如果沒有死鎖發生,並且即使所有進程突然請求對資源的最大需求,也仍然存在某種調度次序能夠使得每一個進程運行完畢,則稱該狀態是安全的(安全狀態一定沒有死鎖發生)。
在這裏插入圖片描述
安全狀態與不安全狀態的區別是,從安全狀態出發,系統能夠保證所有的進程都能完成;而從不安全狀態出發,沒有這樣的保證。

單個資源的銀行家算法

一個小城鎮的銀行家,他向一羣客戶分別承諾了一定的貸款額度,算法要做的是對每一個請求進行檢查,檢查如果滿足了這一需求是否會達到安全狀態。如果能,那麼滿足該需求;如果不能,就推遲對這一請求的滿足。

銀行家算法可以推廣到多個資源的情況,此時可以寫成矩陣的形式,每次判斷一行是否滿足,即一個進程的多個資源都進行檢查。

多個資源的銀行家算法

檢查一個狀態是否安全的算法:

  • 查找右邊的矩陣是否存在一行小於等於向量 A。如果不存在這樣的行,那麼系統將會發生死鎖,狀態是不安全的。
  • 假若找到這樣一行,將該進程標記爲終止,並將其已分配資源加到 A 中。
  • 重複以上兩步,直到所有進程都標記爲終止,則狀態時安全的。

如果一個狀態不是安全的,需要拒絕進入這個狀態。

實際上,死鎖避免是非常困難的,無論是資源軌跡圖還是銀行家算法,都需要事先知道進程運行的過程中需要的最大資源數,這幾乎是不可能實現的。

3.4 死鎖預防

死鎖預防就是在程序運行之前預防發生死鎖,即破壞發生死鎖的 4 個必要條件。死鎖避免可以認爲是在程序執行中動態地避免死鎖發生,而死鎖預防可以說是靜態的方式,杜絕死鎖發生的可能性。

1. 破壞互斥條件:儘量使得資源不被某個進程獨佔。

但有些資源不能同時訪問,如打印機等臨界資源只能互斥使用。所以在某些場合應該保護這種互斥性。

2. 破壞佔有和等待條件:禁止已經持有資源的進程再等待其他資源。一種方式是,在進程開始執行前請求所需的全部資源(預先靜態分配的方法),如果不能滿足,那麼就不分配資源,進行等待。這種方式的問題在於,類似銀行家算法,事先不知道需要多少資源,而且資源利用率不高。另一種方式就是,當一個進程請求資源時,先暫時釋放其當前所佔用的所有資源,然後再嘗試一次獲取所需的全部資源。

這種方式實現簡單,但缺點也顯而易見,系統資源被嚴重浪費,其中有些資源可能僅在運行初期或運行快結束時才使用,甚至根本不使用。而且還會導致“飢餓”現象,當由於個別資源長期被其他進程佔用時,將致使等待該資源的進程遲遲不能開始運行。

3. 破壞不可搶佔條件:允許資源搶佔即可。當然,有的時候資源應當是不可搶佔的。

該策略釋放已獲得的資源可能造成前一階段工作的失效,反覆地申請和釋放資源會增加系統開銷,降低系統吞吐量。這種方法常用於狀態易於保存和恢復的資源,如 CPU 的寄存器及內存資源,一般不能用於打印機之類的資源。

4. 破壞環路等待:一種方法是對資源進行編號,進程在任何時候都可以請求資源,但是所有的請求必須按照資源編號的順序(升序)提出。

這種方法存在的問題是,編號必須相對穩定,這就限制了新類型設備的增加;儘管在爲資源編號時已考慮到大多數作業實際使用這些資源的順序,但也經常會發生作業使甩資源的順序與系統規定順序不同的情況,造成資源的浪費;此外,這種按規定次序申請資源的方法,也必然會給用戶的編程帶來麻煩。

3.5 死鎖處理策略比較

資源分配策略 模式 優點 缺點
死鎖檢測 寬鬆,只要允許就分配資源 定期檢查死鎖是否已經發生 不延長進程初始化時間,允許對死鎖進行現場處理
死鎖避免 是”預防“和”檢測“ 的折中(在運行時判斷是否可能死鎖) 尋找可能的安全允許順序 不必進行剝奪
死鎖預防 保守,寧可資源閒置 一次請求所有資源,資 源剝奪,資源按序分配 適用於做突發式處理的進程,不必進行剝奪

4. 其他相關

4.1 活鎖

活鎖指進程並沒有被阻塞,但由於某些條件沒有滿足,導致一直重複嘗試、失敗、嘗試、失敗。進程仍可以在 CPU 上活動,但 CPU 時間片執行完了之後又下了 CPU,進程沒有任何進展但也沒有阻塞。

活鎖和死鎖的區別:

  • 死鎖有可能自行解開,而死鎖不能
  • 活鎖有 CPU 執行權,而死鎖進程是無法獲得 CPU 執行權的

4.2 飢餓

飢餓:進程無限等待的情況。飢餓 ≠ 死鎖,但是飢餓至少有一個進程的執行被無限期推遲。

產生飢餓的原因:往往是由於資源分配策略的 “不公平性” 導致的,比如短作業優先。此時系統雖然沒有發生死鎖,某些進程也可能會一直得不到 CPU 的使用權而長時間等待。當 “飢餓” 到一定程度,進程任務即使完成也不再具有實際意義時稱該進程被 “餓死”。

飢餓與死鎖都是由於 資源競爭 而引起的。

飢餓與死鎖的差別

  • 進入“飢餓”狀態的進程可以只有一個,而由於循環等待條件而進入死鎖狀態的進程卻必須大於或等於兩個。
  • 處於“飢餓”狀態的進程可以是一個就緒進程,如靜態優先權調度算法時的低優先權進程,而處於死鎖狀態的進程則必定是阻塞進程。
  • 死鎖進程等待的是永遠不會被釋放的資源,而餓死進程等待的是會釋放但不會分配給自己的資源。
  • 死鎖一定發生了循環等待,而餓死不一定。可以通過資源分配圖檢測是否死鎖,但不能檢測是否有進程餓死。

4.3 通信死鎖

當一個進程 A 向 B 發送信息後掛起,需要 B 進程的回覆喚醒時,如果請求信息丟失,A 就會被阻塞以等待回覆,B 會阻塞等待一個向其發送命令的請求,因而發生死鎖。

4.4 兩階段加鎖

這是針對數據庫的一種方法。第一階段中,進程試圖對 所有需要更新的記錄 進行加鎖,一旦某個記錄已經被加鎖,就釋放之前的鎖,從頭進行重試。只有當第一階段所有獲取鎖的行爲都成功,才進行第二階段的更新,否則放棄所有的鎖。


參考文章:
《計算機操作系統》總結五(死鎖)
死鎖 作者:luStar
CS-Notes:死鎖

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