臨界資源
可重用性資源
可重用性資源是一種可供用戶重復使用多次的資源,每一個可重用性資源中的單元只能分配給一個進程使用,不允許多個進程共享。進程在使用可重用性資源的順序是先請求資源,接着使用資源,最后當進程使用完后自己釋放資源。系統中每一類可重用性資源中的單元數目是相對固定的,進程在運行期間既不能創建也不能刪除它。對資源的請求和釋放通常都是利用系統調用來實現的,計算機系統中大多數資源都屬於可重用性資源。
可消耗性資源
可消耗性資源是在進程運行期間由進程動態地創建和消耗的,進程在運行過程中可以不斷地創造可消耗性資源的單元放入緩沖區中,也可以消耗若干個可消耗性資源單元供進程使用。可消耗性資源通常是由生產者進程創建,由消費者進程消耗,最典型的就是用於進程間通信的消息等。
可搶占性資源
可搶占性資源是指某進程在獲得這類資源后,該資源可以再被其它進程或系統搶占。例如優先級高的進程可以搶占優先級低的進程的處理機,或者在內存緊張時可以將一些進程從內存調出到外存上來搶占內存空間。
不可搶占性資源
搶占性資源是一旦系統把某資源分配給該進程后就不能強行收回,只能在進程用完后自行釋放。例如進程使用打印機打印文件,如果此時打印機資源被搶占就會導致完整的文件不能被打印,這樣就無法進行工作了,因此打印機資源不能強行收回。
死鎖
死鎖的定義
死鎖的定義是如果一組進程中的每一個進程都在等待僅由該組進程中的其它進程才能引發的事件,那么該組進程是死鎖的(Deadlock)。
死鎖出現的場合
死鎖通常是源於多個進程對資源的爭奪,對不可搶占資源或對可消耗資源進行爭奪時都可能引起死鎖。
競爭不可搶占性資源引起死鎖
例如系統中有 2 個臨界資源 R1 和 R2,兩個進程 P1 和 P2 的運行都需要 R1 和 R2 資源。如果進程 P1 或者 P2 中有個進程執行得比較快,在另一個進程開始之前就完成了工作並且歸還資源,就不會產生死鎖。
但是若進程 P1 先獲得資源 R1,進程 P2 先獲得資源 R2。后來 P1 又請求資源 R2,但是 R2 已被分配給了 P2 而導致 P1 阻塞。P2 又請求資源 R1,但是 R1 已被分配給了 P1 而導致 P2 阻塞。此時兩個進程都被阻塞,同時它們都希望對方能釋放出自己所需要的資源,最終誰都因不能獲得自己所需的資源去繼續運行,也無法釋放出自己占有的資源陷入僵持狀態。
競爭可消耗資源引起死鎖
例如系統中有 P1、P2、P3 三個進程使用消息通信機制進行通信,m1、m2 和 m3 是發送的消息,屬於可消耗資源。3 個進程的通信方式是 P1 產生 m1 發送給 P2,P2 產生 m2 發送給 P3,P3 產生 m3 發送給 P1 形成一個環。然后它們繼續將信息發送給下一個進程,也就是 P1 產生 m3 發送給 P2,P2 產生 m1 發送給 P3,P3 產生 m2 發送給 P1。
此時由於 P1、P2、P3 三個進程都是先發送消息,再接受其他進程發給自己的消息。但是如果 3 個進程先執行接收操作,由於此時沒有進程發送消息,3 個進程會不斷等待其他進程發消息,同時自己因為沒有收到消息也無法向其他進程發消息。
進程推進順序不當引起死鎖
進程在運行過程中,如果對資源進行申請和釋放的順序不合法也可能導致死鎖。例如系統中有 2 個臨界資源 R1 和 R2,兩個進程 P1 和 P2 的運行都需要 R1 和 R2 資源。若進程 P1 先獲得資源 R1,進程 P2 先獲得資源 R2,2 個進程使用完資源后馬上釋放。后來 P1 又請求資源 R2,P2 又請求資源 R1,此時由於資源有正確釋放所以不會引起死鎖。
如果 2 個進沒有及時釋放資源,而是執行完之后統一釋放就會發生進程死鎖,這樣的進程推進順序就是非法的。
發生死鎖的條件和處理方法
死鎖的條件
進程在運行過程中產生死鎖必須同時具備下面四個必要條件,只要其中任一個條件不成立死鎖就不會發生:
- 互斥條件:進程對所分配到的資源進行排它性使用;
- 請求和保持條件:進程已經保持了至少一個資源,但又提出了對其他已被占用的資源的請求;
- 不可搶占條件:進程已獲得的資源在未使用完之前不能被搶占,只能自己主動釋放;
- 循環等待條件:在發生死鎖時必然存在一個進程一資源的循環鏈。
處理方法
處理死鎖的方法可歸結為四種,四種方法對死鎖的防范程度逐漸減弱,但資源利用率和並發程度逐漸加強。
- 預防死鎖:通過設置某些限制條件,破壞產生死鎖四個必要條件中的一個或幾個;
- 避免死鎖:在資源的動態分配過程中,用某種方法防止系統進入不安全狀態;
- 檢測死鎖:發生死鎖后及時地檢測出死鎖的發生;
- 解除死鎖:當檢測到系統中已發生死鎖時,就采取相應措施將進程從死鎖狀態解除。
- 這里有個疑問,為什么不把檢測死鎖和解除死鎖合並成一個方法?我個人的理解是解除死鎖是建立在檢測出死鎖的情況下,不能用解除死鎖的方法來檢測死鎖,這是 2 個分開的步驟。或者這么說,檢測出死鎖之后可以不解除啊,所以分成 2 種方法。
預防死鎖
預防死鎖的方法是通過破壞產生死鎖的四個必要條件中的一個或幾個,由於互斥條件是非共享設備所必須的,所以預防死鎖主要是破壞產生死鎖的后三個條件。
破壞“請求和保持”條件
破壞“請求和保持”條件需要保證:當一個進程在請求資源時,不能持有不可搶占資源。該保證可通過如下兩個不同的協議實現:
第一種協議
所有進程在開始運行之前,必須一次性地申請其在整個運行過程中所需的全部資源。如果進程獲得了所有需要的資源,在整個運行期間就不會再提出資源要求,從而破壞了“請求”條件。系統在分配資源時只要有一種資源不能滿足進程的要求,就不分配給該進程。由於該進程在等待期間未占有任何資源,於是破壞了“保持”條件。
第一種協議的優點是簡單、易行且安全,但由於進程的某些資源可能僅在短時間內調用,或者根本沒有用過,這就導致了資源的浪費。同時進程可能會因為某個資源被占用而遲遲無法進入就緒狀態,造成了飢餓的發生。
第二種協議
允許一個進程只獲得運行初期所需的資源后開始運行,進程運行過程中再逐步釋放已分配給自己的、且已用畢的全部資源,然后再請求新的所需資源。第二種協議是對第一種的改進,提高了進程的效率、設備的利用率,減少進程發生飢餓的機率。
破壞“不可搶占”條件
當一個進程提出不能被滿足的新的資源請求時,如果它已經占用了某些不可被搶占資源,則它必須釋放已經保持的所有資源。這種協議可以暫時地釋放已占有的資源,從而破壞了“不可搶占”條件。不過這種協議實現起來比較復雜,且需付出很大的代價,因為釋放已有的資源可能導致該進程已經做的工作前功盡棄了。
破壞“循環等待”條件
對系統所有資源類型進行線性排序,並按照合理的方式賦予不同的序號。規定每個進程必須按序號遞增的順序請求資源,如果需要多個同類資源單元則必須一起請求。如果進程又想請求一個序號低的資源時,必須先釋放所有具有相同和更高序號的資源后才能申請。例如可以對掃描儀和打印機進行編號,如果掃描儀的編號小於打印機,則進程只能先申請掃描儀,釋放后再申請打印機。這種策略保證了持有較大編號資源的進程不能逆過來申請編號較小的資源,因此占據了較高序號的資源的進程續申請的資源必然是空閑的,進程可以一直向前推進,破壞了“循環等待”條件。
這種預防死鎖的策略比前兩種策略有着更高的資源利用率和系統吞吐量,但是為系統中各類資源所規定的序號必須相對穩定,限制了新類型設備的增加。同時這種方式需要先對資源的使用的先后順序有預先的估計,但是實際情況下作業使用各類資源的順序與系統規定的順序可能不同,造成對資源的浪費。而且這種方式限制了資源申請的順序,就限制了用戶編寫應用程序的功能,是用戶的程序變得。
避免死鎖
避免死鎖是在資源動態分配過程中,防止系統進入不安全狀態以避免發生死鎖。避免死鎖的限制條件弱於預防死鎖,因此比預防死鎖的系統性能更好。
系統安全狀態
在死鎖避免方法中把系統的狀態分為安全狀態和不安全狀態,當系統處於安全狀態時可避免發生死鎖,反之可能進入到死鎖狀態。安全狀態是指系統能按某種進程推進順序 (P1, P2,……, Pn) 為進程分配資源,使每個進程都可順利地完成,此時稱 (P1, P2,……, Pn) 為安全序列。如果系統無法找到這樣一個安全序列,則稱系統處不安全狀態。避免死鎖的基本思想是確保系統始終處於安全狀態,當有進程請求一個可用資源時,系統需對該進程的請求進行計算,若將資源分配給進程后系統仍處於安全狀態分配資源。
例如假定系統中有三個進程 P1、P2 和 P3,共有 10 個單位的資源,3 個進程分別需要 10、7、4 個單位的資源。假設在 T0 時刻進程 P1、P2 和 P3 已分別獲得 3、3、2 個單位的資源,有 2 個單位的資源未分配。
進程 | 需求 | 已分配 |
---|---|---|
P1 | 10 | 3 |
P2 | 7 | 3 |
P3 | 4 | 2 |
當按照 (P3, P2, P1) 的順序執行時,P1 獲取剩余的 2 個單位資源后執行完畢,釋放資源后有 4 個單位的資源空閑;P2 獲取空閑的 4 個單位資源后執行完畢,釋放資源后有 7 個單位的資源空閑;P3 獲取空閑的 7 個單位資源后執行完畢,此時沒有發生死鎖。因此存在一個安全序列 (P3, P2, P1),在 T0 時刻系統是安全的。如果不按照安全序列分配資源,則系統可能會由安全狀態進入不安全狀態,例如在 T0 時刻系統把剩余 2 台中的 1 台分配給 P2 系統就進入不安全狀態。
銀行家算法
銀行家算法的具體定義和所用的數據結構,在課本已經給了詳細的描述,這里只是簡單地說一說。銀行家算法的思路就是先嘗試把資源分配給一個進程,然后嘗試能否找到一個安全序列,如果能找到就執行該方案,若找不到就放棄試探方案並恢復試探前的狀態。
算法描述
算法使用的數據結構有:
數據結構 | 大小 | 說明 |
---|---|---|
Available | 長度為 m 的一維數組 | 表示還有多少可用資源 |
Max | n * m 矩陣 | 表示各進程對資源的最大需求數 |
Allocation | n * m 矩陣 | 表示已經給各進程分配了多少資源 |
Need | Max - Allocation 矩陣 | 表示各進程最多還需要多少資源 |
Request | 長度為 m 的一位數組 | 表示進程此次申請的各種資源數 |
銀行家算法步驟如下:
其中安全性算法是先檢查當前的剩余可用資源是否能滿足某個進程的最大需求,如果可以就把該進程加入安全序列,並把該進程持有的資源全部回收。不斷重復上述過程,看最終是否能讓所有進程都加入安全序列。
算法樣例
通過一個例子來描述算法,假定系統中有五個進程 {P0, P1,P2, P3, P4} 和三類資源 {A, B, C},各種資源的數量分別為 10、5、7。如果把三類資源的分配數量用一個三維向量表示,在 T0 時刻的資源分配情況如表格所示,此時剩余可用資源為 (3,3,2)。
進程 | 最大需求 | 已分配 | 最多還需要 |
---|---|---|---|
P0 | (7,5,3) | (0,1,0) | (7,4,3) |
P1 | (3,2,2) | (2,0,0) | (1,2,2) |
P2 | (9,0,2) | (3,0,2) | (6,0,0) |
P3 | (2,2,2) | (2,1,1) | (0,1,1) |
P4 | (4,3,3) | (0,0,2) | (4,3,1) |
對進程進行試探操作,用剩余可用資源這個向量和每個進程的“最多還需要”做對比。進程 P1 的最多還需要 (1,2,2) 的每個元素都小於等於 (3,3,2),說明剩余資源可以滿足 P1 的需求。將資源分配給 P1 並加入安全序列,P1 執行完畢后釋放所有資源得到更新后的剩余資源為 (5,3,2)。
進程 | 最大需求 | 已分配 | 最多還需要 |
---|---|---|---|
P0 | (7,5,3) | (0,1,0) | (7,4,3) |
P2 | (9,0,2) | (3,0,2) | (6,0,0) |
P3 | (2,2,2) | (2,1,1) | (0,1,1) |
P4 | (4,3,3) | (0,0,2) | (4,3,1) |
進程 P3 的最多還需要 (0,1,1) 的每個元素都小於等於 (5,3,2),說明剩余資源可以滿足 P3 的需求。將資源分配給 P3 並加入安全序列,P3 執行完畢后釋放所有資源得到更新后的剩余資源為 (7,4,3)。
進程 | 最大需求 | 已分配 | 最多還需要 |
---|---|---|---|
P0 | (7,5,3) | (0,1,0) | (7,4,3) |
P2 | (9,0,2) | (3,0,2) | (6,0,0) |
P4 | (4,3,3) | (0,0,2) | (4,3,1) |
進程 P0 的最多還需要 (7,4,3) 的每個元素都小於等於 (7,4,3),說明剩余資源可以滿足 P0 的需求。將資源分配給 P0 並加入安全序列,P0 執行完畢后釋放所有資源得到更新后的剩余資源為 (7,5,3)。
進程 | 最大需求 | 已分配 | 最多還需要 |
---|---|---|---|
P2 | (9,0,2) | (3,0,2) | (6,0,0) |
P4 | (4,3,3) | (0,0,2) | (4,3,1) |
進程 P2 的最多還需要 (6,0,0) 的每個元素都小於等於 (7,5,3),說明剩余資源可以滿足 P2 的需求。將資源分配給 P2 並加入安全序列,P2 執行完畢后釋放所有資源得到更新后的剩余資源為 (13,5,3)。
進程 | 最大需求 | 已分配 | 最多還需要 |
---|---|---|---|
P4 | (4,3,3) | (0,0,2) | (4,3,1) |
進程 P4 的最多還需要 (4,3,1) 的每個元素都小於等於 (7,5,3),說明剩余資源可以滿足 P2 的需求。將資源分配給 P2 並加入安全序列,P2 執行完畢后所有進程都執行完畢了,得到安全序列 {P1, P3,P0, P2, P4} 說明這種分配資源的方式不會產生死鎖。
死鎖的檢測和解除
系統發生死鎖時,就只能使用死鎖的檢測算法和解除算法進行處理。死鎖檢測算法用於檢測系統狀態,確定系統中是否發生了死鎖。死鎖解除算法是在認定系統中已發生了死鎖,利用該算法可將系統從死鎖狀態中解脫出來。
資源分配圖
系統死鎖可利用資源分配圖來描述,資源分配圖的定義和數據結構中的 G = (N, E) 一樣。其中結點集 N 分為兩個互斥的子集,分別是一組進程結點 P = {P1, P2, …, Pn} 和一組資源結點 R = {R1, R2, …, Ro}。E 中的一個資源請求邊 e = {Pi,Rj} 都連接着 P 中的一個結點和 R 中的一個結點,由進程 Pi 指向資源 Rj 表示進程 Pi 請求一個單位的 Rj,由資源 Rj 指向進程 Pi 表示把一個單位的資源 Rj 分配給進程 Pi。
死鎖定理
利用把資源分配圖加以簡化的方法,來檢測當系統是否處於死鎖狀態。如果系統中剩余的可用資源數足夠滿足進程的需求,則這個進程暫時不會阻塞可以繼續執行。如果這個進程執行結束了把資源歸還系統,就可能使某些正在等待資源的進程被激活,並順利地執行下去。這些被激活的進程執行完了之后又會歸還一些資源,這樣可能又會激活另外一些阻塞的進程。不斷執行后歸還進程,如果最終能消除所有邊,就稱這個圖是可完全簡化的,此時一定沒有發生死鎖。如果最終不能消除所有邊,那么此時就是發生了死鎖。例如上面的資源分配圖的化簡過程如下:
並不是系統中所有的進程都是死鎖狀態,用死鎖檢測算法化簡資源分配圖后連着邊的那些進程就是死鎖進程。
死鎖的解除
如果利用死鎖檢測算法檢測出在系統中已發生了死鎖,可以立即通知操作員以人工方法處理死鎖,也可以使用死鎖解除算法。常采用解除死鎖的 3 種方法是:
- 搶占資源:掛起某些死鎖進程並搶占它的資源,將這些資源分配給其他的死鎖進程,這種方式需要注意避免飢餓;
- 終止(或撤消)進程:終止(或撤消)系統中的一個或多個死鎖進程,並剝奪資源直至打破循環環路;
- 進程回退:讓一個或多個死鎖進程回退到足以避免死鎖的地步,要求系統要設置還原點。
終止所有死鎖進程是一種最簡單的方法,但是其中有些進程可能已經接近結束,一旦被終止會導致前功盡棄。稍微溫和的方法是按照某種順序逐個地終止進程,直至有足夠的資源以打破循環等待。但該方法所付出的代價也可能很大,因為每終止一個進程都需要用死鎖檢測算法確定系統死鎖是否已經被解除,若未解除還需再終止另一個進程。同時在采取逐個終止進程策略時,還涉及到應采用什么策略來度量“代價最小”,一般需要考慮的因素有:
- 進程的優先級的大小;
- 進程已執行的時間;
- 進程的截止時間;
- 進程已經使用資源的多少,還需要多少資源;
- 進程的性質是交互式的還是批處理式的。
參考資料
《計算機操作系統(第四版)》,湯小丹 梁紅兵 哲鳳屏 湯子瀛 編著,西安電子科技大學出版社
一句話+一張圖說清楚——銀行家算法