操作系統精髓與設計原理學習筆記六:併發(死鎖和飢餓)

本章講述在併發處理中通常需要解決的兩個問題:死鎖和飢餓

處理死鎖的三種常用方法:預防、檢測和避免

經典問題:哲學家就餐問題


一、死鎖原理

可以把死鎖定義爲一組互相競爭系統資源或進行通信的進程間的“永久”阻塞。

當一組進程中的每個進程都在等待某個事件(典型的情況是等待所請求的資源被釋放),而只有在這組進程中的其他被阻塞的進程纔可以觸發該事件,這時就稱這組進程發生死鎖。因爲沒有事件能夠被觸發,故死鎖是永久性的。死鎖問題並沒有一種有效的通用解決方案。所有死鎖都涉及兩個或多個進程之間對資源需求的衝突。

資源通常可分爲兩類:可重用資源和可消耗資源。

1 可重用資源

可重用資源是指一次只能供一個進程安全地使用,而且不會由於使用而耗盡的資源。進程得到資源單元,後來又釋放這些單元,供其他進程再次使用。

可重用資源的例子包括處理器、IO通道、內存和外存、設備,以及諸如文件、數據庫和信號量之類的數據結構。

2 可消耗資源

可消耗資源師指可以被創建(生產)和銷燬(消耗)的資源。通常對某周類型可消耗資源的數目沒有限制,一個無阻塞的生成進程可以創建任意數目的這類資源。當消費進程得到一個資源時,該資源就不存在了。

可消耗資源的例子有中斷、信號、消息和IO緩衝區中的信息。

3 死鎖的條件

死鎖的三個必要條件:

1)互斥:一次只有一個進程可以使用一個資源。其他進程不能訪問已分配給其他進程的資源;

2)佔有且等待:當一個進程等待其他進程時,繼續佔有已經分配的資源;

3) 不可搶佔:不能強行搶佔進程已佔有的資源。

對死鎖的產生,還需要第四個條件:

4)循環等待:存在一個封閉的進程鏈,使得每個進程至少佔有此鏈中下一個進程所需要的一個資源。

第四個條件實際上是前三個條件的潛在結果。假設前三個條件存在,可能發生的一系列事件會導致不可解的循環等待。


二、死鎖預防

死鎖預防策略是試圖設計一種系統來排除發生死鎖的可能性。可以把死鎖預防方法分成兩類。一種是間接的死鎖預防方法,即防止前面勒出的桑必要條件中任何一個的發生;一種是直接的死鎖預防方法,即防止循環等待的發生。


三、死鎖避免

死鎖避免允許三個必要條件,但通過明智的選擇,確保永遠不會到達死鎖點,因此死鎖避免比死鎖預防允許更多的併發。

在死鎖避免中,是否允許當前的資源分配請求時通過判斷該請求是否可能導致死鎖來決定的。因此,死鎖避免需要知道將來的進程資源請求的情況。

兩種死鎖避免方法:

1)如果一個進程的請求會導致死鎖,則不啓用此進程;

2)如果一個進程增加的資源請求會導致死鎖,則不允許此分配。

死鎖避免的優點是它不需要死鎖預防中的搶佔和回滾進程,並且比死鎖預防的限制少。


四、死鎖檢測

死鎖預防策略是非常保守的,他們通過限制訪問資源和在進程上加強約束來解決死鎖的問題。

死鎖檢測策略則完全相反,它不限制資源訪問或約束進程行爲。對於死鎖檢測來說,只要有可能,被請求的資源就被分配給進程。操作系統週期性的執行一個算法檢測前面的條件。


五、哲學家就餐問題

算法必須保證互斥(沒有兩位哲學家同時使用一把叉子),同時還要避免死鎖和飢餓。

哲學家就餐問題可以視爲當應用程序中包含併發線程的執行時,協調處理共享資源的一個有代表性的問題。

1 基於管程的解決方案:

管程包含了兩個過程。get_forks函數用於表示哲學家取他左邊和右邊的叉子。如果至少有一把叉子不可用,那麼哲學家進程就會在條件變量的隊伍中等待。這可讓另外的哲學家進入這個管程。release-forks函數用來標誌兩把叉子可用。


六、UNIX的併發機制

UNIX爲進程間的通信和同步提供了各種機制。這裏,最幾種的包括:

管道,消息,共享內存,信號量,信號。

1)管道:管道在創建時獲得一個固定大小的字節數。當一個進程試圖往管道中寫時,如果有足夠的空間,則寫請求被立即執行;否則該進程被阻塞。類似的,如果一個讀進程試圖讀取的字節數多於當前管道中的字節數時,它也被阻塞;否則讀請求被立即執行。操作系統強制實施互斥,即一次只能有一個進程可以訪問管道。

2)共享內存:共享內存是UNIX提供的進程間通信手段中速度最快的一種。

3)信號:信號時用於向一個進程通知發生異步事件的機制。信號類似於硬件中斷,但沒有優先級,即內核平等的對待所有的信號。


七、Linux內核併發機制

Linux包含了在其他UNIX系統中出現的所有併發機制,其中包括管道、消息、共享內存和信號。Linux還包含一套豐富的併發機制,這套機制是特別爲內核態線程準備的。換言之,他們是用在內核中的並非機制,提供內核代碼執行中的併發性。

1 原子操作

Linux提供了一組操作以保證對變量的原子操作。這些操作能夠用來避免簡單的競爭條件。原子操作執行時不會被打斷或被幹涉。在單處理器上,線程一旦啓動原子操作,則從操作開始到結束這段時間內,線程不能被中斷。此外,在多處理器系統中,該原子操作所針對的變量時被鎖住的,以免被其他的進程訪問,知道此原子操作執行完畢。

2 自旋鎖

在Linux中保護臨界區最常見的技術是自旋鎖。在同一時刻,只有一個線程能獲得自旋鎖。其他任何企圖獲得自旋鎖的線程將一直進行嘗試,直到獲得了該鎖。

本質上,自旋鎖建立在內存區中的一個整數上,任何線程進入臨界區之前都必須檢查該整數。如果該值爲0,則線程設置該值爲1,然後進入臨界區。如果該值非0,則該線程繼續檢查該值,直到它爲0。

自旋鎖很容易實現,但有一個缺點,即在鎖外面的線程以忙等待的方式繼續執行。

使用自旋鎖的基本形式如下:

spin_lock(&lock)

/*臨界區*/

spin_unlock(&lock)


八、小結

死鎖是指一組徵用系統資源或互相通信的進程被阻塞的現象。這種阻塞是永久的,除非操作系統採取某些非常的行動(殺死或回滾進程)。死鎖可能涉及可重用資源或可消耗資源。

處理死鎖通常有三種方法:預防、檢測盒避免。死鎖預防通過確保死鎖的一個必要條件不會滿足,保證不會發生死鎖。如果操作系統總是同意資源請求,則需要進行死鎖檢測。操作系統必須週期性的檢查死鎖,並採取行動打破死鎖。死鎖避免設計分析新的資源請求,以確定它是否會導致死鎖,並且只有當不可能發生死鎖時才同意該請求。

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