死鎖&死鎖的產生條件&死鎖的避免
1、死鎖的概念:
對於鎖這個字,大家一定不會陌生,因為我們生活中就存在著大量的鎖,它們各個方面發揮著它的作用,現在世
界中,就是阻止某些人做某些事,例如,門鎖就是阻止除了屋主之外的人進入這個房子,你進入不到這個房子,也就
不能使用房子裡面的東西。
死鎖 (deallocks): 是指兩個或兩個以上的程序(執行緒)在執行過程中,因爭奪資源而造成的一種互相等待的現
象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進
程(執行緒)稱為死鎖程序(執行緒)。 由於資源佔用是互斥的,當某個程序提出申請資源後,使得有關程序(執行緒)在
無外力協助下,永遠分配不到必需的資源而無法繼續執行,這就產生了一種特殊現象死鎖。
一種交叉持鎖死鎖的情形,此時執行程式中兩個或多個執行緒發生永久堵塞(等待),每個執行緒都在等待被其它線
程佔用並堵塞了的資源。例如,如果執行緒 1 鎖住了記錄 A 並等待記錄 B,而執行緒 2 鎖住了記錄 B 並等待記錄 A,這
樣兩個執行緒就發生了死鎖現象。在計算機系統中 , 如果系統的資源分配策略不當,更常見的可能是程式設計師寫的程式有
錯誤等,則會導致程序因競爭資源不當而產生死鎖的現象。簡單的說一個程序持有了一個鎖之後,在臨界區內又去申
請該鎖,它將不得不等待該鎖被釋放,但因為它本身在等待申請該鎖,所以永遠不會有機會釋放鎖並得到鎖,最終結
果就是死鎖。因為很多鎖都不是可遞迴鎖,所以不要嘗試在一個執行緒內多次申請同一個鎖。
原理:競爭資源,程序推進順
2、死鎖產生的原因:
1)系統資源不足;
2)程序(執行緒)推進的順序不恰當;
3)資源分配不當。
如果系統資源充足,程序的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則就會因爭奪有限的資源而陷入
死鎖;其次,程序執行推進順序與速度不同,也可能產生死鎖。
3、死鎖的形成場景:
1)忘記釋放鎖:在申請鎖和釋放鎖之間直接return
2)單執行緒重複申請鎖:一個執行緒,剛出臨界區,又去申請資源。
3)多執行緒多鎖申請:兩個執行緒,兩個鎖,他們都已經申請了一個鎖了,都想申請對方的鎖
4、死鎖的條件:
1)互斥條件:簡單的說就是程序搶奪的資源必須是臨界資源,一段時間內,該資源只能同時被一個程序所佔有。
2)請求和保持條件:當一個程序持有了一個(或者更多)資源,申請另外的資源的時候發現申請的資源被其他程序所持有,當前程序阻塞,但不會是放自己所持有的資源。
- 3)不可搶佔條件:程序已經獲得的資源在未使用完畢的情況下不可被其他程序所搶佔
在很多情況下這些條件都是合乎要求的。例如,為確保結果的一致性和資料庫的完整性,互斥是非常有必要的。同理,不能隨意的進行資源搶佔。比如,當涉及資料資源時,必須提供回滾恢復機制(rollback recovery machanism)以支援搶佔資源,這樣才能把程序和他的資源恢復到以前適當的狀態,使得程序最終可以重複他的動作。
前三個條件都是死鎖存在的必要條件,但不是充分條件。對死鎖的產生還需要第四個條件:
4)迴圈等待條件:存在一個封閉的程序鏈,使得每個程序至少佔有此鏈中下一個程序所需要的一個資源。
這裡所說的資源不僅包括硬體資源或者其他的資源,還包括鎖,鎖也是一種資源,鎖的爭用也會導致死鎖
5、預防死鎖:
預防死鎖的辦法就是破壞死鎖的四個必要條件,只要破壞了條件,死鎖自然就不會產生了,簡單的描述一下破壞四個
條件的思想
(1)破壞請求和保持條件:
1)所有程序在開始執行之前,必須一次性獲得所有資源,如果無法獲得完全,釋放已經獲得的資源,等待;
2)所有程序在開始執行之前,只獲得初始執行所需要的資源,然後在執行過程中不斷請求新的資源,同時釋放自己
已經用完的資源。
相比第一種而言,第二種方式要更加節省資源,不會浪費(因為第一種可能出現一種資源只在程序結束用那麼一小
下,但卻從頭到尾都被佔用,使用效率極低),而且,減少了程序飢餓的情況。
(2)破壞不可搶佔條件:
說起來簡單,只要當一個程序申請一個資源,然而卻申請不到的時候,必須釋放已經申請到的所有資源。但是做
起來很複雜,需要付出很大的代價,加入該程序已經持有了類似印表機(或者其他的有必要連續工作的)這樣的設
備,申請其他資源的時候失敗了,必須釋放印表機資源,但是人家用印表機已經用過一段時間了,此時釋放印表機資
源很可能造成之後再次是用印表機時兩次執行的資訊不連續(得不到正確的結果)。
(3)破壞迴圈等待條件:
設立一個規則,讓程序獲取資源的時候按照一定的順序依次申請,不能違背這個順序的規則。必須按照順序申請
和釋放,想要申請後面的資源必須先把該資源之前的資源全部申請,想要申請前面的資源必須先把該資源之後的資源
(前提是已獲得)全部釋放。
(4)破壞互斥條件:
沒法破壞,是資源本身的性質所引起的。
6、死鎖避免:在死鎖預防中通過約束資源請求,防止四個死鎖條件中至少一個的發生。這可以通過防止發生三個必要策略條件中的一個(互斥、佔有且等待、非搶佔)間接完成,也可以通過防止迴圈等待直接完成,但這都會導致低效的資源使用和低效的程序執行。死鎖避免則相反,它允許三個必要條件,但通過明智的選擇,確保永遠不會到達死鎖點,因此死鎖避免需要更多地併發。在死鎖避免中,是否允許當前的資源分配請求是通過判斷該請求是否可能導致死鎖來決定的。因此,死鎖避免需要知道將來的程序資源請求的情況。
(1)有序資源分配法:
這種演算法資源按某種規則系統中的所有資源統一編號(例如印表機為1,、磁帶機為2、磁碟為3、等等),申請時必須以上升的次序。
採用有序資源分配法:R1的編號為1,R2的編號為2,;PA:申請次序應是:R1,R2;PB:申請次序應是:R1,R2;這樣就破壞了環路條件,避免了死鎖的發生。
(2)銀行家演算法(最具有代表性)
該演算法需要檢查申請者對資源的最大需求量,如果系統現存的各類資源可以滿足申請者的請求,就滿足申請者的
請求。這樣申請者就可以很快完成其計算,然後釋放它佔用的資源,從而保證了系統中的所有程序都能完成,所以可
避免死鎖的發生。
演算法原理:把作業系統看作是銀行家,作業系統管理的資源相當於銀行家管理的資金,程序向作業系統請求分配資
源相當於使用者向銀行家貸款。為保證資金的安全,銀行家規定:
(1) 當一個顧客對資金的最大需求量不超過銀行家現有的資金時就可接納該顧客;
(2) 顧客可以分期貸款,但貸款的總數不能超過最大需求量;
(3) 當銀行家現有的資金不能滿足顧客尚需的貸款數額時,對顧客的貸款可推遲支付,但總能使顧客在有限的時間裡
得到貸款;
(4) 當顧客得到所需的全部資金後,一定能在有限的時間裡歸還所有的資金.
7、死鎖的檢測和解除:使用類似銀行家演算法的方式就可以簡單的檢測死鎖
死鎖解除:
1)終止程序(簡單粗暴),就是字面上的,你們死鎖了,我就把你們一起殺掉,缺點就是如果一個程序跑了很長時
間,但是被殺了,還得從頭來。
2)逐個終止程序,按照某種順序,挨個殺死程序,每殺一個程序就去看看死鎖解除了沒有(每殺一個程序都會釋放
一些資源,如果釋放好粗來的資源解決了死鎖問題,就沒必要再濫殺無辜了),沒解除就繼續殺。
第二種方式顯然人性化了許多,但是按照某種順序顯得很朦朧,這裡的某種順序就是指死鎖解除演算法,有很多,
這裡不再贅述。
注意:
寫程式時應該儘量避免同時獲得多個鎖,如果一定有必要這麼做,則有一個原則:如果所有執行緒在需要多個鎖時都按相同的先後順序(常見的是按Mutex變數的地址順序)獲得鎖,則不會出現死。假如一個程式中用到鎖1、鎖2、鎖3,它們所對應的Mutex變數的地址是鎖1<鎖2<鎖3,那麼 所有執行緒在需要同時獲得2個或3個鎖時都應該按鎖1、鎖2、鎖3的順序獲得。如果要為所有的鎖確定一個先後順序比較困難,則應該儘量使用pthread_mutex_trylock呼叫代替pthread_mutex_lock 呼叫以免死鎖。