死鎖

資源
需要排他性使用的對象。正因爲資源具有排他性,發生死鎖的情況通常是,兩個進程都佔有完成任務所需要的所有資源的一部分,並且都不願意釋放。

可搶佔資源和不可搶佔資源
可搶佔資源是指從擁有它的進程中搶佔而不會產生任何副作用。存儲器就是典型的可搶佔資源。
不可搶佔資源是指,搶佔進程佔有的資源會導致計算失敗。

使用資源的步驟:
1、請求資源
2、使用資源
3、釋放資源
比如一個文件資源,當它正在被使用時,發出open調用的進程被阻塞,一直到文件的使用者關閉該文件爲止。該進程在阻塞的這段時間,會處在這樣一個循環中:請求資源,休眠,再請求資源。

資源獲取
用戶管理資源的方法是,爲每個資源分配一個信號量,信號量初始化爲1,每次執行down操作來獲取資源,釋放資源後,執行up操作。
● 一個進程情況下,不存在資源競爭,無死鎖。

#使用信號量保護一個資源
typedef int semaphore;
semaphore resource_1;

void process_A(void){
    down(&resoure_1);//資源1可用數量減1
        use_resource_1();
    up(&resource_1);//資源1可用數量加1
}

#使用信號量保護兩個資源
typedef int semaphore;
semaphore resource_1;
semaphore resource_2;

void process_B(void){
    down(&resource_1);
    down(&resource_2);
        use_both_resources();
    up(&resource_2);
    up(&resource_1);
}

● 兩個進程情況下,存在資源競爭,有可能死鎖。

#無死鎖代碼
typedef int semaphore;
semaphore resource_1;
semaphore resource_2;

void process_A(void){
    down(&resource_1);
    down(&resource_2);
        use_both_resources();
    up(&resource_2);
    up(&resource_1);
}

void process_B(void){
    down(&resource_1);
    down(&resource_2);
        use_both_resources();
    up(&resource_2);
    up(&resource_1);
}
#有可能出現死鎖代碼,A進程擁有resource_1,B進程擁有resource_2,兩個進程都阻塞
typedef int semaphore;
semaphore resource_1;
semaphore resource_2;

void process_A(void){
    down(&resource_1);
    down(&resource_2);
        use_both_resources();
    up(&resource_2);
    up(&resource_1);
}

void process_B(void){
    down(&resource_2);
    down(&resource_1);
        use_both_resources();
    up(&resource_1);
    up(&resource_2);
}

死鎖

如果一個進程集合中的每個進程都在等待只能由該進程集合中的其他進程才能引發的事件,那麼,該進程集合就是死鎖的。
假設進程只含有一個線程,並且被阻塞的進程無法被中斷喚醒(無中斷條件)。死鎖進程集合中的每個進程都在等待其他進程已經佔有的資源,並永遠等待下去。
考慮一個例子,當兩列火車在相同的軌道上相向行駛時,只有一條軌道,一旦他們在彼此前面,沒有一輛火車可以移動。當有兩個或多個進程保存一些資源並等待其他資源佔有的資源時,操作系統中會發生類似的情況。例如,在下圖中,進程1佔有資源1並等待由進程2佔有的資源2,而進程2等待資源1。
死鎖

資源死鎖的條件:
● 互斥條件。每個資源要麼已經分配給一個進程,要麼就是可用的。
● 佔有和等待條件。已經得到資源的進程可以再請求新的資源。
● 不可搶佔條件。已經分配給一個進程的資源不能強制性被搶佔,必須擁有它的資源顯式釋放。
● 死鎖環路條件。死鎖發生時,有兩個或兩個以上進程組成一個環路,該環路里的每個進程都在等待下一個進程佔有的資源。

死鎖建模

資源分配圖
資源分配圖
圓形表示進程,方形表示資源。箭頭指向資源,表示進程在請求資源;箭頭指向進程,表示進程佔有該資源。
資源分配圖
根據給定的資源請求/釋放隊列,按次序畫資源分配圖,每一步後檢查資源分配圖是否包含有向環路,如果包含環路,則會發生死鎖;否則,沒有死鎖。

處理死鎖的策略

有三種處理死鎖的方式

1)忽略死鎖:如果死鎖非常罕見,那麼讓它發生並重新啓動系統。這是windows和unix採取的方法。
2)死鎖檢測和恢復:發生死鎖,然後進行搶佔處理一次。
3)死鎖預防或避免:想法是不讓系統進入死鎖狀態。

1、鴕鳥算法

即忽略該問題,不做任何檢測和恢復。

2、 死鎖檢測和恢復

如何檢測死鎖

傳統的Windows操作系統不會因爲時間和空間消耗進程而進行死鎖恢復。實時操作系統使用死鎖恢復。

  • 每種類型資源個數只有一個的死鎖檢測
    維護一個隊列,對資源分配圖中的每個節點,以它爲一個棵數的根結點,進行深度優先搜索,入隊,如果隊列中出現重複的結點,說明有環,則有死鎖;資源分配圖中每個節點的隊列沒有重複的結點,則沒有環。
    死鎖例子

  • 每種類型資源個數爲多個的死鎖檢測
    1)尋找一個系統可用資源滿足請求資源的線程,用Pi向量表示,Pi<=A
    2)Pi運行完,釋放它佔有的資源到可用資源池,可用資源池用A向量,A=A+Pi
    3)再次尋找爲運行的資源請求可被滿足的線程,如果沒有這樣的線程,說明死鎖發生
    死鎖檢測算法的四個數據
    恆等式:Ej=Pj+Aj,Pj表示進程j已分配的資源,Aj表示進程j可用的資源,Ej表示現有資源。

死鎖檢測的例子
死鎖檢測算法
E向量表示系統中四種資源總量,A向量表示四種資源當前數量,C是已經分配給三個進程的資源數量,R表示三個進程的請求資源的數量。首先,,進程3的資源請求能被滿足,運行進程3;進程3終止後,釋放進程3的資源,接着進程2被滿足,運行進程2;等到進程2釋放它的資源,進程1資源請求被滿足,最後運行進程1.

何時去檢測死鎖
一種方法是,每當有資源請求時取檢測。這種方法太佔用CPU時間了
另一種方法是,每隔k分鐘檢測一次,或者當CPU利用率降到某個閾值時,去檢測死鎖。

檢測到死鎖如何恢復
1、利用搶佔恢復
人工干預進程管理的一種方式,通常由管理員根據該資源本身的特性,在不通知原進程的情況下,強行剝奪走資源,通過這種方法恢復比較困難,而且不現實。
2、利用回滾恢復
週期性的對進程進行檢查點檢查,檢查點檢查就是將某一個時刻的進程的狀態寫入一個文件。文件中包括存儲映像,還有資源狀態。新的檢查點覆蓋舊的檢查點。發生死鎖時,擁有所需資源的進程恢復到一個較早的狀態,這個狀態下,它還沒佔有資源,然後將所需資源分配給死鎖進程。
3、通過殺死進程恢復
一種方法是殺掉環中的一個進程,如果殺死一個不夠,那就繼續殺死別的進程直到破壞死鎖。
另一種方法是犧牲環外的一個進程,釋放它的資源。

死鎖避免
資源軌跡圖
資源軌跡圖
橫軸表示進程P在運行,進程Q被掛起(1、2軌跡)。縱軸表示進程Q在運行,進程P被掛起(5、6軌跡)。
因爲進程的執行不能後退,所以軌跡總是向上或向右的方向,3、4軌跡不可避免的會進入死鎖,因爲資源互斥使用規則決定了P和Q都不可能進入單斜線區域,所以只能進入交叉斜線區域(死鎖區).

銀行家算法是資源分配和死鎖避免算法,用於對所有請求資源的進程進行測試,檢查安全狀態,如果在授權請求後,系統仍保持在安全狀態,允許請求,如果它們不是安全狀態, 不允許進程發出的請求。

安全狀態和不安全狀態
這兩種狀態前提是都沒有死鎖產生。
安全狀態
如果存在一個調度次序使得每一個進程運行完畢,則爲安全狀態

不安全狀態
不安全狀態不是死鎖,因爲進程不一定要最大需求量的資源。但是任何調度次序都不能保證工作完成,比如b)中的狀態。

銀行家算法的輸入
1.每個過程最多需要的資源數量。
2.每個進程目前分配資源數量。
3.系統中最大可用的資源。
請求僅在以下條件下授予:
1.如果進程的請求小於等於該進程的最大值。
2.如果進程的請求小於等於系統中的可用資源。

單個資源的銀行家算法
銀行家算法
銀行家算法是對每一個請求進行檢查。檢查如果滿足這一請求是否會進入安全狀態,即是否有足夠的資源滿足某一進程。如果可以,滿足請求;否則,推遲滿足請求。

多個資源的銀行家算法
銀行家算法
檢查一個狀態是否安全的算法如下:
1)檢查右邊矩陣是否存在一個進程,其仍需要的資源小於或等於A,如果不存在,則系統會死鎖;
2)如果存在這樣一個進程,則把該進程標記爲終止,並將資源加到向量A上。
3)重複以上兩步
這樣的算法有兩個結果,要麼所有的進程都標記爲終止,其初始狀態是安全的,要麼所有進程的資源都不能滿足,系統發生死鎖。
實際上, 銀行家算法缺乏使用價值,一是進程所需資源的最大值很難在運行前確定,二是進程數量在操作系統運行過程中不斷變化,三是有些資源可能突然間變得不可用。

死鎖預防
我們可以通過消除上述四種條件來防止死鎖。
1、破壞互斥條件
資源不被一個進程獨享,那麼死鎖肯定不會產生。但破壞互斥條件是很困難的,因爲一些資源(如打印機)本質上是不可共享的。

2、破壞佔有和等待條件
禁止已佔有資源的進程再請求其他資源。
第一種實現方法是,直到進程所需的全部資可用,系統才分配給該進程。但很多進程直到運行時才知道它需要多少資源,而且這種方法資源利用率不是最優的
第二種實現方法是,當一個進程請求資源時,先釋放它佔有的資源,然後再嘗試一次性獲得所需的所有資源。這種方案很可能導致飢餓

3、破壞不可搶佔條件
在其他高優先級進程需要資源時,搶佔資源。但搶佔資源,會引起混亂,虛擬化的方式可以解決這個問題,例如,只有打印機守護進程才能訪問真正的打印機。

4、破壞環路等待條件
一種實現方法是,對所有資源進程統一編號。進程必須按照資源編號的升序順序提出請求,也就是說,一個進程首先要查看自己佔有資源的最高編號,不能申請低於最高編號的資源。例如,如果p1進程被分配r5資源,下次如果p1請求r4,r3,它們的編號小於r5,則不會給予這樣的請求,所以只會請求超過r5的資源。
實現的困難在於很難找到每個人都滿意的編號次序。

小結

死鎖小結

通信死鎖
假設處於網絡中的兩個進程,進程A向進程B發送消息,由於網絡阻塞等原因,消息丟失,則A一直等待B回覆,而B無法收到請求,A和B進程都阻塞。這種通信中斷死鎖,可以引入超時機制來解決。進程A發送消息的同時,啓動計時器,設定一個時間,如果在A確認收到回覆前到時,則重新發送消息。

活鎖
活鎖指的是任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致一直重複嘗試,失敗,嘗試,失敗。在UNIX中,進程表容納的進程數有最大限制,如果由於進程表滿,而fork失敗,程序會等待一段隨機長的時間,再執行一次fork

飢餓
由於資源分配策略不當等原因,導致某些進程一直無法獲得資源而不能繼續執行。比如,有些進程永遠無法獲得CPU。可以用先來先服務的分配資源策略來避免飢餓。

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