一.死锁原理
死锁可以定义为一组相互竞争系统资源或进行通信的进程间的“永久”阻塞。当一组进程中每个线程都在等待某个事件(典型的就是等待所请求的资源被释放),而只有这组进程的其他被阻塞的进程才可以触发该事件的发生,这是这时就称为这组进程发生死锁。因为没有事件能够被触发,所以死锁是永久性的。
我们生活中常见的死锁问题就是交通死锁。如图所示,显示了一个十字路口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();
}