線程:死鎖

一.死鎖原理

死鎖可以定義爲一組相互競爭系統資源或進行通信的進程間的“永久”阻塞。當一組進程中每個線程都在等待某個事件(典型的就是等待所請求的資源被釋放),而只有這組進程的其他被阻塞的進程纔可以觸發該事件的發生,這是這時就稱爲這組進程發生死鎖。因爲沒有事件能夠被觸發,所以死鎖是永久性的。
我們生活中常見的死鎖問題就是交通死鎖。如圖所示,顯示了一個十字路口4輛車幾乎同時到達了,並且相互交叉的停了下來,如果四輛車都要筆直的通過十字路口,那麼他們對資源的要求如下:
1車需要象限a和b,
2車需要象限b和c,
3車需要象限c和d,
4車需要象限d和a。
這裏寫圖片描述
這裏造成了一種潛在的死鎖,因爲對每輛車需要前進的資源都能滿足,所以僅僅是潛在的死鎖。最終只要只有一輛車能前進,就不會死鎖。
但是如果每輛車都不管,繼續直行,那麼每輛車都會佔據一個資源,這就會發生真正的死鎖。

二 . 死鎖的條件

死鎖有三個必要條件:

1.互斥。

一次只有一個進程可以使用一個資源,其他進程不能訪問分配給其他進程的資源。

2.佔有且等待。

當一個進程等待其他進程時,繼續佔有已經分配的資源。

3.不可搶佔。

不能強行搶佔進程已有的資源。
在很多情況下這些條件都是很容易達到的。例如,我們所學的消息隊列,信號量,互斥是有必要的,因爲它保證了數據的完整且正確。
前三個條件都是死鎖存在的必要條件,不是充分條件,對死鎖的產生還需要第四個條件:

4.循環等待。

存在一個封閉的進程鏈,使得每個進程至少佔有此鏈中下一個進程所需要的資源。
第四個條件是前三個條件的潛在結果,即假設前三個條件存在,可能發生的一系列會導致發生不可解的循環等待。這實際上就是死鎖。前三個條件是死鎖的必要條件,第四個條件是死鎖的充分條件:
這裏寫圖片描述

三.處理死鎖的方法

1)死鎖預防。
簡單來講就是設計一種系統來排除死鎖的可能性。可以把死鎖預防分爲兩大類:一種間接死鎖預防,即防止前面三個條件的任何一個的發生;另一種是直接死鎖預防,即防止循環等待的發生。下面我們來具體分析:

互斥:一般來說,地鐵一個條件不可能禁止,如果要對資源進行互斥訪問,那麼系統必須支持互斥,就有可能發生死鎖,比如文件,允許多個讀訪問,但只能允許互斥的寫訪問,這種情況下,如果多個進程申請權限,就有可能發生死鎖。

佔有且等待:爲預防佔有且等待,可要求進程一次性的申請所有的資源,並且阻塞這個進程直到所有請求都得到滿足。這個方法有可能很低效,有可能一個進程被阻塞很長時間申請資源,但是其實只需要一部分資源就可以繼續執行;另外分配的資源可能長時間不被使用,但也不會被釋放,也就不能被其他進程使用;另一個問題就是一個進程不可能事先就知道他所需要的所有資源。

不可搶佔:有幾種方法可以預防這個條件,如果一個進程繼續申請資源被拒絕,那麼它要釋放最初佔用的資源,如果有必要,再次申請;另外如果一個進程申請當前被另一個進程佔用的資源,則操作系統可以強行佔用另一個系統的資源,要求他釋放。

循環等待:可以定義資源的線性順序來預防。如果一個進程已經分配到了某一類型的資源,那麼它只能申請排在這一類型後面的資源。

2)死鎖避免

死鎖避免允許三個必要的條件,但通過明智的選擇,永遠不會達到死鎖點,這就是死鎖避免。它比死鎖預防允許更多的併發,例如:如果一個進程請求會導致死鎖,則不啓動此進程。另如果一個進程申請的資源會導致進程死鎖,則不允許分配資源。

3)模擬死鎖檢測算法

1. 輸入:

“資源分配表”文件,每一行包含資源編號、進程編號兩項(均用整數表示,並用空格分隔開),記錄資源分配給了哪個進程。

“進程等待表”文件,每一行包含進程編號、資源編號兩項(均用整數表示,並用空格分隔開),記錄進程正在等待哪個資源。

下面是一個示例:

資源分配表:

1 1

2 2

3 3

進程等待表:

1 2

2 3

3 1

2. 處理要求:

程序運行時,首先提示“請輸入資源分配表文件的文件名:”;再提示“請輸入進程等待表文件的文件名:”。

輸入兩個文件名後,程序將讀入兩個文件中的有關數據,並按照死鎖檢測算法進行檢測。

3. 輸出要求:

第一行輸出檢測結果:有死鎖 或 無死鎖。

第二行輸出進程循環等待隊列,即進程編號(如果有死鎖)。

4. 文件名約定

提交的源程序名字:resourceXXX.c或者resourceXXX.cpp(依據所用語言確定)

輸入文件名字:可由用戶指定

結果輸出到resultXXX.txt中

其中:XXX爲賬號。

5. 死鎖檢測算法:當任一進程Pj申請一個已被其他進程佔用的資源ri時,進行死鎖檢測。檢測算法通過反覆查找進程等待表和資源分配表,

來確定進程Pj對資源ri的請求是否導致形成環路,若是,便確定出現死鎖。

6. 測試說明:測試教師將事先準備好一組文件(格式爲*.txt),從中爲每個程序隨機指定一至三個作爲輸入文件

(被測試者需從鍵盤輸入指定文件的文件名),並查看程序輸出結果。

本程序包括:死鎖檢測算法

#include<stdio.h>

#include<iostream.h>

#include<string.h>

const int MAXQUEUE=100; //定義表的最大行數

typedef struct node{

int resource;

int process;

}cell;

cell occupy[MAXQUEUE];

int occupy_quantity;

cell wait[MAXQUEUE];

int wait_quantity;

//初始化函數

void initial()

{

int i;

for(i=0;i<MAXQUEUE;i++){

occupy[i].process=-1;

occupy[i].resource=-1;

wait[i].process=-1;

wait[i].resource=-1;

}

occupy_quantity=0;

wait_quantity=0;

}

//讀數據文件

int readData()

{

FILE *fp;

char fname[20];

int i;

cout<<"請輸入資源分配表文件的文件名:"<<endl;

strcpy(fname,"10trouble1.txt");

//cin>>fname;

if((fp=fopen(fname,"r"))==NULL){

cout<<"錯誤,文件打不開,請檢查文件名:)"<<endl;

return 0;

}

else{

while(!feof(fp)){

fscanf(fp,"%d %d",&occupy[occupy_quantity].resource,&occupy[occupy_quantity].process);

occupy_quantity++;

}

}

cout<<"請輸入進程等待表文件的文件名:"<<e

ndl;

strcpy(fname,"10trouble2.txt");

//cin>>fname;

if((fp=fopen(fname,"r"))==NULL){

cout<<"錯誤,文件打不開,請檢查文件名:)"<<endl;

return 0;

}

else{

while(!feof(fp)){

fscanf(fp,"%d %d",&wait[wait_quantity].process,&wait[wait_quantity].resource);

wait_quantity++;

}

}

//輸出所讀入的數據

cout<<endl<<endl<<"輸出所讀入的數據"<<endl;

cout<<"━━━━━━━━━━━━━━━━━━━━━━━"<<endl;

cout<<"資源分配表"<<endl;

cout<<"資源編號 進程編號"<<endl;

for(i=0;i<occupy_quantity;i++){

cout<<" "<<occupy[i].resource<<" "<<occupy[i].process<<endl;

}

cout<<"───────────────────────"<<endl;

cout<<"進程等待表"<<endl;

cout<<"進程編號 資源編號"<<endl;

for(i=0;i<wait_quantity;i++){

cout<<" "<<wait[i].resource<<" "<<wait[i].process<<endl;

}

return 1;

}

//檢測

void check()

{

int table[MAXQUEUE][MAXQUEUE];

int table1[MAXQUEUE][MAXQUEUE];

int i,j,k;

int flag,t,p;

int max_process;

//初始化表格

for(i=0;i<MAXQUEUE;i++){

for(j=0;j<MAXQUEUE;j++){

table[i][j]=0;

table1[i][j]=0;

}

}

//先找到進程最大編號

max_process=-1;

for(i=0;i<occupy_quantity;i++){

if(occupy[i].process>max_process){

max_process=occupy[i].process;

}

}

for(i=0;i<wait_quantity;i++){

if(wait[i].process>max_process){

max_process=wait[i].process;

}

}

for(i=0;i<wait_quantity;i++){

for(j=0;j<occupy_quantity;j++){

if(wait[i].resource==occupy[j].resource){

table[wait[i].process][occupy[j].process]=1;

table1[wait[i].process][occupy[j].process]=1;

}

}

}

cout<<"初始等待佔用表:"<<endl;

for(i=0;i<max_process+1;i++){

for(j=0;j<max_process+1;j++){

cout<<table[i][j]<<" ";

}

cout<<endl;

}

cout<<endl;

for(i=0;i<max_process+1;i++){

for(j=0;j<max_process+1;j++){

for(k=0;k<max_process+1;k++){

table[i][j]=table[i][j]||(table[i][k]&&table[k][j]);

}

}

}

cout<<"檢測後的等待佔用表:"<<endl;

for(i=0;i<max_process+1;i++){

for(j=0;j<max_process+1;j++){

cout<<table[i][j]<<" ";

}

cout<<endl;

}

flag=-1;

for(i=0;i<max_process+1;i++){

if(table[i][i]==1){

flag=i;

break;

}

}

cout<<endl<<endl<<"檢測結果"<<endl;

cout<<"───────────────────"<<endl;

if(flag!=-1){

cout<<"存在死鎖"<<endl;

cout<<"進程循環等待隊列:";

p=flag; //存在進程循環等待隊列的那一進程

//進程循環等待隊列中的所有進程是table表中的這一行是1的進程,只是順序要再確定

t=1;

while(t){

cout<<p<<" ";

for(j=0;j<max_process+1;j++){

if(table1[p][j]==1){

if(table[j][flag]==1){

p=j;

break;

}

}

}

if(p==flag)t=0;

}

cout<<flag<<endl;

}

else{

    cout<<"不存在死鎖"<<endl;

}

}


void main()

{

    int flag;

    version();

    initial();

    flag=readData();

if(flag)check();

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