操作系統學習筆記(七) 死鎖


一、資源

把需要排他性使用的對象稱為資源。資源可以是硬件也可以是軟件,比如打印機或者數據庫中的一個加鎖記錄。

資源可以分為兩類:可搶占資源和不可搶占資源。

可搶占資源:可以從擁有它的進程中搶占而不產生副作用。

不可搶占資源:不引起相關的計算失敗的情況下,無法把它從占有它的進程處搶占過來。

搶占這個詞,在進程和線程調度時就提到了這個概念,那時是進程或者線程可以搶占CPU,即搶占式調度。存儲器也可以搶占,如內存換頁。

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

二、死鎖

死鎖的概念:如果一個進程集合中,每個進程都在等待只能由該集合中其他進程才能引發的事件,那么該進程集合就是死鎖的。

死鎖並不僅僅發生在資源上,資源死鎖只是一種。

資源死鎖的四個必要條件

(1)互斥條件。每個資源要么已經分配給了一個進程,要么就是可用的。

(2)占有和等待條件。已經得到了某個資源的進程可以再請求新的資源。

(3)不可搶占條件。已經分配給一個進程的資源不能被搶占,只能由占有它的進程顯式地釋放。

(4)環路等待條件。死鎖發生時,系統中一定有由兩個或以上的進程組成的一條環路,該環路中的每個進程都在等待着下一個進程所占有的資源。

可以用一個有向圖來表示資源分配的情況。用圓形節點表示進程,方形表示資源。從資源節點到進程節點的有向邊表示該資源被請求、並被進程占用。由進程到資源節點的有向邊表示進程正在請求該資源,並且因為請求資源而導致進程被阻塞,處於等待該資源的狀態。

這樣,根據環路等待條件,一旦在某個時候有向圖中出現了兩個或兩個以上進程組成的環路,就會導致死鎖的發生。

下圖是《現代操作系統》中的一個例子:

現在有三個進程A、B、C,它們需要R、S、T三個資源中的其中兩個,具體參見圖上方的信息。操作系統可以選取任意一個就緒的進程執行。

我們可以看到,在第一種執行順序中,每個進程都占有了一個資源,從而在請求第二個資源的時候,每個進程都進入了阻塞態,最后沒有進程可以執行。同時注意到,資源圖確實構成了環路。

在第二種執行順序中,進程A率先獲取了所有它需要的資源R和S,盡管C因為請求資源R而進入了阻塞態,但是A使用完資源后,釋放了R和S,最終C可以繼續執行下去,沒有構成死鎖。

處理死鎖的策略:

(1)忽略該問題。一般稱為鴕鳥算法,即躲起來視而不見。

(2)檢測死鎖並恢復。讓死鎖發生,檢測它們是否發生,一旦發生死鎖,就采取行動解決問題。

(3)仔細對資源進行分配,動態地避免死鎖。也稱為死鎖避免。

(4)通過破壞引起死鎖的四個必要條件,防止死鎖的發生。也稱為死鎖預防。

三、死鎖檢測和恢復

采用這種辦法的時候,系統並沒有試圖阻止死鎖的發生,而是允許死鎖發生,檢測到死鎖后,采取措施進行恢復。

檢測:我們知道,死鎖的一個必要條件是存在兩個及以上進程組成的環路。通過檢測有向圖環路,是一種檢測死鎖存在的方法。

當每種類型存在多個資源時,檢測可能會復雜許多。

恢復的方法:

(1)利用搶占恢復。死鎖發生的必要條件,其中一個就是不可搶占。如果允許搶占,那么就可以破壞死鎖條件。

(2)利用回滾:周期性對進程進行檢查點檢查,一旦發現了死鎖,就回滾到一個較早的檢查點上。

(3)通過殺死進程:這個方法是最顯而易見的。殺死一個進程可以釋放它占有的資源,如果仍然不行那么久繼續殺死其他進程直到打破死鎖。

四、死鎖避免

1、資源軌跡圖

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

2、銀行家算法

在介紹銀行家算法之前,先引入一個安全狀態與不安全狀態的概念。

如果沒有死鎖發生,並且即使所有的進程突然請求對資源的最大需求,也仍然存在某種調度次序,能使得每個進程運行完畢,則稱該狀態是安全的。

該圖中,a狀態下是安全的,因為按照b-e的順序,整個過程能完成而不會產生死鎖。只要系統仔細進行調度,就能夠避免死鎖。

該圖中,a狀態是安全的,但是b中把資源先分配給了A進程1個,導致后面可能會出現。注意,不安全狀態並不一定是死鎖,b中的狀態,依然可以運行一段時間,B進程甚至可以運行完成,直到沒有空閑資源才會發生死鎖。如果A能主動釋放資源,可能也不會出現死鎖。

安全狀態與不安全狀態的區別是,從安全狀態出發,系統能夠保證所有的進程都能完成;而從不安全狀態出發,沒有這樣的保證。

單個資源的銀行家算法:對每一個請求進行檢查,檢查如果滿足了這一需求是否會達到安全狀態。如果能,那么滿足該需求;如果不能,就推遲對這一請求的滿足。

例如,上面圖中,銀行家可以只滿足B客戶的需求,拖延其他客戶的需求,因而可以讓B先完成,然后釋放B的4個資源。有了這4個資源,就可以給B或者D分配所需要的資源。

不安全狀態不一定產生死鎖,不過如果一旦一個進程需要最大資源,就會導致死鎖,銀行家算法避免這種風險。

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

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

五、死鎖預防

死鎖避免可以認為是在程序執行中動態地避免死鎖發生,而死鎖預防可以說是靜態的方式,杜絕死鎖發生的可能性。

只要能破壞死鎖發生的四個必要條件之一,那么死鎖就不會發生。

1、破壞互斥條件

盡量使得資源不被某個進程獨占。比如打印機的假脫機打印,就是盡量避免一個進程獨占打印機,而是把要打印的文件存入一個假脫機目錄,然后通過一個守護進程管理打印機進行打印。

2、破壞占有和等待條件

禁止已經持有資源的進程再等待其他資源。

一種方式是,在進程開始執行前請求所需的全部資源,如果不能滿足,那么就不分配資源,進行等待。這種方式的問題在於,類似銀行家算法,事先不知道需要多少資源,而且資源利用率不高。

另一種方式就是,當一個進程請求資源時,先暫時釋放其當前所占用的所有資源,然后再嘗試一次獲取所需的全部資源。

3、破壞不可搶占條件

這個比較好理解,允許資源搶占即可。當然,有的時候資源應當是不可搶占的。

4、破壞環路等待條件

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

六、其他問題

1、兩階段加鎖

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

2、通信死鎖

這種情況其實是很常見的。當一個進程A向B發送信息后掛起,需要B進程的回復喚醒時,如果請求信息丟失,A就會被阻塞以等待回復,B會阻塞等待一個向其發送命令的請求,因而發生死鎖。

通信死鎖不涉及資源,不能通過合理調度資源來避免。一般通信協議會解決這種問題,包括超時重傳等技術。

3、活鎖

輪詢(忙等待)的方式,在有時是有效的,因為掛起進程等待的開銷很大。考慮如下程序:

 

enter_region()通過輪詢獲取資源,假設A獲得了資源1,B獲得了資源2,那么兩個進程都不會阻塞,而是不停地進行輪詢以獲取資源。兩個進程總是運行完系統分配的時間片,沒有阻塞但是不會取得進展。

4、飢餓

飢餓的概念,其實與死鎖和活鎖差別比較大。考慮進程調度,基於優先級的調度中,如果總是有高優先級的進程就緒,那么一個低優先級的進程可能長時間無法上CPU運行。這就是飢餓現象,可以考慮通過動態優先級機制,可以動態提高長時間得不到運行的進程的優先級,從而使它可以運行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM