一.死鎖原理
死鎖可以定義爲一組相互競爭系統資源或進行通信的進程間的“永久”阻塞。當一組進程中每個線程都在等待某個事件(典型的就是等待所請求的資源被釋放),而只有這組進程的其他被阻塞的進程纔可以觸發該事件的發生,這是這時就稱爲這組進程發生死鎖。因爲沒有事件能夠被觸發,所以死鎖是永久性的。
我們生活中常見的死鎖問題就是交通死鎖。如圖所示,顯示了一個十字路口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();
}