死鎖檢測與恢復

注:本文主要參考自<<現代操作系統>>6.3,6.4節

死鎖一文中,我們提到死鎖問題有四種解決方案.本文將主要介紹第二種方法:死鎖檢測與恢復.

此處簡要提及一下第一種方法,忽略死鎖.如果程序員可以推斷死鎖發生的概率非常小,且發生死鎖後不會造成嚴重後果.則可以考慮忽略死鎖,畢竟解決死鎖問題不僅需要編寫額外代碼,死鎖解決方案本身也會對程序性能造成影響.例如我們即將介紹的死鎖檢測與恢復方法中.需要持續判斷當前是否發生了死鎖,這是一個不小的開銷.

死鎖檢測與恢復

使用這種技術,我們並不試圖區阻止死鎖的發生,而是及時檢測死鎖是否發生.當檢測到死鎖發生時,嘗試進行恢復以消除死鎖.

每種類型只有一個資源

我們首先介紹每種類型只有一個資源的情形.例如, 當前僅有一臺打印機可用.此時所有需要使用打印機的進程都要競爭這一臺打印機.對於該問題,我們可以使用在死鎖一節中提到的資源分配圖解決.

在每一次資源請求後,我們都需要檢測當前資源分配圖中是否存在環路,如果存在環路,則表示當前環路上的進程發生了死鎖. 因此可以使用有向圖中迴路檢測算法來判斷是否發生死鎖.

每種類型有多個資源

在此類情形中,每種類型有多個資源,一個進程也可能需要獲取一種類型的多個資源.特別注意,在後面分析中,我們假設進程一次性請求其所需的所有資源.
假設共有mm種資源,我們使用向量E=(E1,E2,...,Em)E=(E_1, E_2, ..., E_m)表示每種資源的數量.例如E1=4E_1=4表示第一種資源的總數爲4.使用向量A=(A1,A2,...,Am)A=(A_1, A_2, ..., A_m)表示當前資源的可用數量.例如A1=2A_1=2表示當前第一種資源剩餘兩個可用.假設共有nn個進程,使用矩陣Cn×mC_{n\times m}表示進程已分配資源.其中C[1][2]C[1][2]表示進程1已經佔有的第二種資源的個數.使用矩陣Rn×mR_{n\times m}表示當前進程的請求資源數, .例如, R[1][2]R[1][2]表示當前時刻進程1正請求的第二種資源的數量.

基於以上,我們可以使用一種標記算法來判斷當前時刻進程中是否發生死鎖, 如果發生死鎖,涉及到哪些進程.在初始時,所用進程都未被標記.
使用如下算法對進程進行標記:

  1. 如果當前進程尚未被標記,且RiAR_i \le A, 則當前進程請求的資源可以被分配.如果找不到符合要求的進程,則算法終止.
  2. 分配資源, Ci+=RiC_i += R_i, A=RiA -= R_i.
  3. 進程執行完畢後,將釋放其佔用的資源,因此A+=CiA += C_i
  4. 轉回步驟1.
    在算法執行完成後,如果有進程尚未被標記,則系統中存在死鎖, 其中未標記的進程爲死鎖涉及的進程.

接下來通過一個簡單的例子講解標記算法.
在這裏插入圖片描述

當前共有三個進程, 此時進程1與進程2都無法被標記,因爲AR1A \le R_1, AR2A \le R_2, 但進程3所需的資源可以被滿足.因此進程3被標記.在進程3執行完成後,將釋放它佔用的資源, 此時A=(2,2,2,0)A = (2, 2, 2, 0), 再次執行標記步驟,此時進程1的資源請求與進程2的資源請求均能被滿足,因此係統中不存在死鎖.

何時執行死鎖檢測

現在我們知道了如何檢測死鎖(至少是在這種預先知道靜態資源請求的情況下),但問題在於何時去檢測它們。一種方法是每當有資源請求時去檢測。毫無疑問越早發現越好,但這種方法會佔用昂貴的CPU時間。另一種方法是每隔k分鐘檢測一次,或者當CPU的使用率降到某一域值時去檢測。考慮到CPU使用效率的原因,如果死鎖進程數達到一定數量,就沒有多少進程可運行了,所以CPU會經常空閒。

從死鎖中恢復

當我們檢測到系統中存在死鎖後,就需要考慮如何對系統進行恢復,以消除死鎖.此處介紹3種可能的方法.

1.利用搶佔恢復

在某些情況下,可能會臨時將某個資源從它的當前所有者那裏轉移到另一個進程。許多情況下,尤其是對運行在大型主機上的批處理操作系統來說,需要人工進行干預。

比如,要將激光打印機從它的持有進程那裏拿走,管理員可以收集已打印好的文檔並將其堆積在一旁。然後,該進程被掛起(標記爲不可運行)。接着,打印機被分配給另一個進程。當那個進程結束後,堆在一旁的文檔再被重新放回原處,原進程可重新繼續工作。

在不通知原進程的情況下,將某一資源從一個進程強行取走給另一個進程使用,接着又送回,這種做法是否可行主要取決於該資源本身的特性。用這種方法恢復通常比較困難或者說不太可能。若選擇掛起某個進程,則在很大程度上取決於哪一個進程擁有比較容易收回的資源。

2.利用回滾恢復

如果系統設計人員以及主機操作員瞭解到死鎖有可能發生,他們就可以週期性地對進程進行檢查點檢查(checkpointed)。進程檢查點檢查就是將進程的狀態寫入一個文件以備以後重啓。該檢查點中不僅包括存儲映像,還包括了資源狀態,即哪些資源分配給了該進程。爲了使這一過程更有效,新的檢查點不應覆蓋原有的文件,而應寫到新文件中。這樣,當進程執行時,將會有一系列的檢查點文件被累積起來。

假設,檢測到進程1與進程2構成死鎖.進程1需要的資源A此時被進程2佔用.我們準備通過重新分配資源滿足進程1,從而消除死鎖.此時,我們需要檢查進程2的檢查點,將其恢復到某一檢查點文件上, 在該檢查點上,進程2尚未佔用資源A. 通過對進程2進程復位,其在檢查點後的所有工作將被清除.然後我們可以把資源A分配給進程1.如果復位後的進程2試圖重新獲得對該資源的控制,它必須等到該資源可用時爲止, 至少需要在進程1執行完畢並釋放該資源後.

3.通過殺死進程恢復

最直接也是最簡單的解決死鎖的方法是殺死一個或若干個進程。一種方法是殺掉環中的一個進程。如果走運的話,其他進程將可以繼續。如果這樣做行不通的話,就需要繼續殺死別的進程直到打破死鎖環。

另一種方法是選一個環外的進程作爲犧牲品以釋放該進程的資源。在使用這種方法時,選擇一個要被殺死的進程要特別小心,它應該正好持有環中某些進程所需的資源。比如,一個進程可能持有一臺繪圖儀而需要一臺打印機,而另一個進程可能持有一臺打印機而需要一臺繪圖儀,因而這兩個進程是死鎖的。第三個進程可能持有另一臺同樣的打印機和另一臺同樣的繪圖儀而且正在運行着。殺死第三個進程將釋放這些資源,從而打破前兩個進程的死鎖。

有可能的話,最好殺死可以從頭開始重新運行而且不會帶來副作用的進程。比如,編譯進程可以被重複運行,由於它只需要讀入一個源文件和產生一個目標文件。如果將它中途殺死,它的第一次運行不會影響到第二次運行。

另一方面,更新數據庫的進程在第二次運行時並非總是安全的。如果一個進程將數據庫的某個記錄加1,那麼運行它一次,將它殺死後,再次執行,就會對該記錄加2,這顯然是錯誤的。

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