數據庫死鎖問題及銀行家算法

在操作系統的設計中,我們往往無法迴避一個問題,那就是進程對有限資源的佔用問題。在計算機系統中有很多獨佔性的資源,在任一時刻,它們都只能被一 個進程使用。常見的有打印機、磁帶驅動器等。例如:兩個進程同時打印會引起打印混亂。鑑於此,操作系統全都具有授權一進程(臨時)獨佔地訪問某一種資源的 能力。

在很多情形中,需要一個進程獨佔地訪問若干種資源而不是一種。例如將一個大文件由磁帶拷貝至打印機,進程需要同時訪問磁帶驅動器和打印機,並且不允 許其他進程這時訪問它們。在只有一個進程的系統中,該進程可以要求任何它所需要的資源,然後進行工作。但是,在一個多道程序系統中,就有可能出現嚴重的問 題。例如,兩個進程分別準備打印一個非常大的磁帶文件。進程A申請打印機,並得到授權。進程B申請磁帶機,也得到授權。現在,A申請磁帶機,但該請求在B 釋放磁帶機前會被拒絕。不幸的是,B非但不放棄磁帶機,而且去申請打印機,而A在申請到磁帶機之前也不會釋放打印機。這時,兩個進程都被阻塞,並且保持下 去,這種狀況就是死鎖(deadlock).

下面系統闡述一下死鎖的定義:所謂死鎖,就是多個進程循環等待它方佔有的資源而無限期的僵持下去的局面。顯然,如果沒有外力的作用,那麼死鎖涉及到的各個進程都將永遠處於封鎖狀態。

計算機系統產生死鎖的根本原因就是資源有限且操作不當。即:一種原因是系統提供的資源太少了,遠不能滿足併發進程對資源的需求。這種競爭資源引起的 死鎖是我們要討論的核心。例如:消息是一種臨時性資源。某一時刻,進程A等待進程B發來的消息,進程B等待進程C發來的消息,而進程C又等待進程A發來的 消息。消息未到,A,B,C三個進程均無法向前推進,也會發生進程通信上的死鎖。

我們可以對資源進行以下分類:
可重用資源(reusable resource):每個時刻只有一個進程使用,但不會耗盡,在宏觀上各個進程輪流使用。如CPU、主存和輔存、I/O通道、外設、數據結構如文件、數據庫和信號量。有可能剝奪資源:由高優進程剝奪低優進程,或OS核心剝奪進程。

P1
P2
... ...
Request(A) <a> Request(B) <a>
Request(B) <b> Request(A) <b>
Request(B) Request(A)
Request(A) Requsst(B)

可重用資源死鎖
死鎖發生:雙方都擁有部分資源,同時在請求對方已佔有的資源。如次序:P1<a> P2<a> P1<b> P2<b>

非可剝奪資源(consumable resource):可以動態生成和消耗,一般不限制數量。如硬件中斷、信號、消息、緩衝區內的數據。

P1

P2

... ...
Receive(P2,M); <a> Receive(P1,Q); <a>
Send(P2,N); <b> Send(P1,R); <b>

非可剝奪資源死鎖

死鎖發生:雙方都等待對方去生成資源,如次序:P1<a> P2<a>

總的來說,死鎖和不可剝奪資源有關。我們討論的重點放在不可剝奪資源。

使用一資源事件的順序如下:申請資源,使用資源,釋放資源。

另一種原因是由於進程推進順序不合適引發的死鎖。資源少也未必一定產生死鎖。就如同兩個人過獨木橋,如果兩個人都要先過,在獨木橋上僵持不肯後退,必然會 因競爭資源產生死鎖;但是,如果兩個人上橋前先看一看有無對方的人在橋上,當無對方的人在橋上時自己才上橋,那麼問題就解決了。所以,如果程序設計得不合 理,造成進程推進的順序不當,也會出現死鎖。

下面簡述一下產生死鎖的必要條件 :

如果在計算機系統中同時具備下面四個必要條件時,那麼會發生死鎖。換句話說,只要下面四個條件中有一個不具備,系統就不會出現死鎖。

1.互斥條件:即某個資源在一段時間內只能由一個進程佔有,不能同時被兩個或兩個以上的進程佔有。

2.不可搶佔條件:進程所獲得的資源在未使用完畢之前,資源申請者不能強行地從資源佔有者手中奪取資源,而只能由該資源的佔有者進程自行釋放。

3.佔有且申請條件:進程至少已經佔有一個資源,但又申請新的資源;由於該資源已被另外進程佔有,此時該進程阻塞;但是,它在等待新資源之時,仍繼續佔用已佔有的資源。

4.循環等待條件 :存在一個進程等待序列{P1,P2,…,Pn},其中P1等待P2所佔有的某一資源,P2等待P3所佔有的某一資源,……,而Pn等待P1所佔有的某一資源,形成一個進程循環等待環。

面我們提到的這四個條件在死鎖時會同時發生。也就是說,只要有一個必要條件不滿足,則死鎖就可以排除。

處理死鎖的基本方法可歸結爲以下3種:

方法
資源分配策略
各種可能模式
主要優點
主要缺點
預防
Prevention
保守的;寧可資源閒置 一次請求所有資源<條件1>
資源剝奪
<條件3>
資源按序申請
<條件4>
適用於作突發式處理的進程;不必剝奪
適用於狀態可以保存和恢復的資源
可以在編譯時(而不必在運行時)就進行檢查
效率低;進程初始化時間延長
剝奪次數過多;多次對資源重新起動
不便靈活申請新資源
避免
Avoidance
是“預防”和“檢測”的折衷(在運行時判斷是否可能死鎖 尋找可能的安全的運行順序 不必進行剝奪 必須知道將來的資源需求;進程可能會長時間阻塞
檢測
Detection
寬鬆的;只要允許,就分配資源 定期檢查死鎖是否已經發生 不延長進程初始化時間;允許對死鎖進行現場處理 通過剝奪解除死鎖,造成損失

關於死鎖的預防、檢測與恢復,不是本文討論的話題,感興趣的讀者可翻閱相關資料查找到這些信息。在這裏,我們着重談一談死鎖的避免。

我們說死鎖預防是排除死鎖的靜態策略,它使產生死鎖的四個必要條件不能同時具備,從而對進程申請資源的活動加以限制,以保證死鎖不會發生;而死鎖的避免是 排除死鎖的動態策略,它不限制進程有關申請資源的命令,而是對進程所發出的每一個申請資源的命令加以動態地檢查,並根據檢查結果決定是否進行資源分配。就 是說,在資源分配過程中若預測有發生死鎖的可能性,則加以避免。這種方法的關鍵是確定資源分配的安全性。 那麼,是否存在一種算法,總能做出正確的選擇從而避免死鎖呢?答案是肯定的,但條件是必須事先獲得一些特定的信息。

Dijkstra 於1965年提出了一個經典的避免死鎖的算法----銀行家算法(banker’s algorithm)。

其模型基於一個小城鎮的銀行家,他向一羣客戶分別承諾了一定金額的貸款,而他知道不可能所有客戶同時都需要最大的貸款額。在這裏,我們可將客戶比作 進程,銀行家比作操作系統。銀行家算法就是對每一個客戶的請求進行檢查,檢查如果滿足它是否會引起不安全狀態。假如是,那麼不滿足該請求;否,那麼便滿 足。

怎樣得知一個狀態是否安全呢?

所謂系統是安全的,是指系統中的所有進程能夠按照某一種次序分配資源,並且依次地運行完畢,這種進程序列{P1,P2,…,Pn}就是安全序列。如果存在這樣一個安全序列,則系統是安全的;如果系統不存在這樣一個安全序列,則系統是不安全的。

更通俗一點地講,檢查狀態是否安全的方法是看它是否有足夠的剩餘資源滿足一個距最大需求最近的客戶。如果有,那麼這比投資被認爲是能夠收回的,然後接着檢查下一個距最大需求最近的客戶,如此反覆下去。如果所有投資最終都被收回,那麼該狀態是安全的,最初的請求可以批准。
需要指出的是,不安全狀態並不一定引起死鎖,因爲客戶並不一定申請其最大貸款額度,但銀行家不敢抱有這種僥倖心理。

在此,我們引入了兩個向量:Resourse(資源總量)、Available(剩餘資源量) 以及兩個矩陣:Claim(每個進程的最大需求量)、Allocation(已爲每個進程分配的數量)。它們共同構成了任一時刻系統對資源的分配狀態。

向量模型:

R1

R2
R3

矩陣模型:

 
R1
R2
P1    
P2    
P3    

這裏,我們設置另外一個矩陣:各個進程尚需資源量(Need),可以看出

Need = Claim – Allocation

因此,我們可以這樣描述銀行家算法:

設Request[i]是進程Pi的請求向量。如果Request[i , j]=k,表示Pi需k個Rj類資源。當Pi發出資源請求後,系統按下述步驟進行檢查:

(1) if (Request[i]<=Need[i]) goto (2);
        else error(“over request”);
(2) if (Request[i]<=Available[i]) goto (3);
        else wait();

(3) 系統試探性把要求資源分給Pi(類似回溯算法)。並根據分配修改下面數據結構中的值。

Available[i] = Available[i] – Request[i] ;

Allocation[i] = Allocation[i] + Request[i];

Need[i] = Need[i]-Request[i];

(4) 系統執行安全性檢查,檢查此次資源分配後,系統是否處於安全狀態。若安全,才正式將資源分配給進程以完成此次分配;若不安全,試探方案作廢,恢復原資源分配表,讓進程Pi等待。

系統所執行的安全性檢查算法可描述如下:

設置兩個向量:Free、Finish

工作向量Free是一個橫向量,表示系統可提供給進程繼續運行所需要的各類資源數目,它含有的元素個數等於資源數。執行安全算法開始時,

Free = Available.

標記向量Finish是一個縱向量,表示進程在此次檢查中中是否被滿足,使之運行完成,開始時對當前未滿足的進程做Finish[i] = false;當有足夠資源分配給進程(Need[i]<=Free)時,Finish[i]=true,Pi完成,並釋放資源。

(1)從進程集中找一個能滿足下述條件的進程Pi

① Finish[i] == false(未定)

② Need[i] <= Free (資源夠分)

(2)當Pi獲得資源後,認爲它完成,回收資源:

Free = Free + Allocation[i] ;

Finish[i] = true ;

Go to step(1);

試探此番若可以達到Finish[0..n]:=true,則表示系統處於安全狀態,然後再具體爲申請資源的進程分配資源。否則系統處於不安全狀態。

我們還舉銀行家的例子來說明:設有客戶A、B、C、D,單一資源即爲資金(R)。

下列狀態爲安全狀態,一個安全序列爲:C->D->B->A

A 1 6
B 1 5
C 2 4
D 4 7

Available = (2) ; Resourse = (10) ;

可以看出,銀行家算法從避免死鎖的角度上說是非常有效的,但是,從某種意義上說,它缺乏實用價值,因爲很少有進程能夠在運行前就知道其所需資源的最 大值,而且進程數也不是固定的,往往在不斷地變化(如新用戶登錄或退出),況且原本可用的資源也可能突然間變成不可用(如磁帶機可能壞掉)。因此,在實際 中,如果有,也只有極少的系統使用銀行家算法來避免死鎖。

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