死锁是指两个或两个以上的进程在执行的过程中,因争夺资源而造成的一种互相等待的现象。
产生死锁的四个必要条件:
1、互斥条件:一个资源每次只能被一个进程使用。
2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。(资源的部分分配)
3、不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺。
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
上述四个条件是产生死锁的四个必要条件,只要上述四个之中有一个不满足,就不会发生死锁。
根据上述条件,列出三条预防措施:
1、采用资源静态分配的策略,破坏“部分分配”条件。
2、允许进程剥夺使用其他进程占有的资源,从而破坏“不可剥夺”条件。
3、采用资源有序分配方法,破坏“环路”条件。
注意:互斥条件没法被破坏。
死锁避免算法里最著名的算法就是Dijkstra的银行家算法。
银行家算法:
基本思想是分配资源之前,判断系统是否是安全的;若是,才分配。它是最具有代表性的避免死锁的算法。
设进程cusneed提出请求REQUEST [i],则银行家算法按如下规则进行判断。
(1)如果REQUEST [cusneed] [i]<= NEED[cusneed][i],则转(2);否则,出错。
(2)如果REQUEST [cusneed] [i]<= AVAILABLE[cusneed][i],则转(3);否则,出错。
(3)系统试探分配资源,修改相关数据:
AVAILABLE[i]-=REQUEST[cusneed][i];
ALLOCATION[cusneed][i]+=REQUEST[cusneed][i];
NEED[cusneed][i]-=REQUEST[cusneed][i];
(4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。
安全性检查算法:
(1)设置两个工作向量Work=AVAILABLE; FINISH(全为false)
(2)从进程集合中找到一个满足下述条件的进程,
FINISH==false;
NEED<=Work;
如找到,执行(3);否则,执行(4)
(3)设进程获得资源,可顺利执行,直至完成,从而释放资源。
Work+=ALLOCATION;
Finish=true;
GOTO(2)
(4)如所有的进程Finish= true,则表示安全;否则系统不安全。
银行家算法的代码实例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 5 //进程个数
#define M 3 //资源种类
int main()
{
int i,j;
int a;
int Request[M];//请求资源距阵
int Allocation[N][M];//已经分配资源矩阵
int Need[N][M];//需求矩阵
int Available[M];//可利用资源量
//输入已经分配的资源
printf("请输入已经分配的资源:\n");
for(i=0;i<N;i++)
{
printf("请输入第 %d 个进程已经分配的资源:",i+1);
for(j=0;j<M;j++)
scanf("%d",&Allocation[i][j]);
}
//输入各个进程还需要的资源
printf("\n请输入各个进程还需要的资源:\n");
for(i=0;i<N;i++)
{
printf("请输入第 %d 个进程还需要的资源:",i+1);
for(j=0;j<M;j++)
scanf("%d",&Need[i][j]);
}
//输入可以利用的资源数据
printf("\n请输入可以利用的资源数据:");
for(i=0;i<M;i++)
scanf("%d",&Available[i]);
while(1)
{
start:printf("\n\n请输入是第几个进程发出资源请求:");
scanf("%d",&a);//输入的是第几个进程的请求
printf("\n第 %d 个进程资源请求数:",a);
for(i=0;i<M;i++)
scanf("%d",&Request[i]);
//银行家算法第一步
for(i=0;i<M;i++)
{
if(!(Request[i]<=Need[a-1][i]))
{
printf("P%d 非法请求!",a);
printf("不可以分配给 P%d 进程!",a);
goto start;
}
}
//银行家算法第二步
for(i=0;i<M;i++)
{
if(!(Request[i]<=Available[i]))
{
printf("P%d 进程阻塞!",a);
printf("不可以分配给 P%d 进程!",a);
goto start;
}
}
//银行家算法第三步:试探性分配
for(i=0;i<M;i++)
{
Available[i]=Available[i]-Request[i];
Allocation[a-1][i]=Allocation[a-1][i]+Request[i];
Need[a-1][i]=Need[a-1][i]-Request[i];
}
//银行家算法第四步:安全性检查
int Finish[N]={0,0,0,0,0};
int Work[M];
for(i=0;i<M;i++)
Work[i]=Available[i];
int flag=1;
while(flag==1)
{
for(i=0;i<N;i++)
{
for(j=0;j<M;j++)
{
if(Finish[i]==0 && Need[i][j]<=Work[j])
{
if(j==M-1)
{
Finish[i]=1;
printf("\nP%d 进程安全性检查通过",i+1);
for(j=0;j<M;j++)
Work[j]=Work[j]+Allocation[i][j];
}
}
continue;
}
}
int sum=0;
for(i=0;i<N;i++)
sum=sum+Finish[i];
if(sum==5)
flag=0;
}
for(i=0;i<N;i++)
{
if(Finish[i]==0)
{
printf("试探性分配不成功!安全性检查不通过!");
printf("\n不可以分配给 P%d 进程!",a);
Available[i]=Available[i]+Request[i];
Allocation[a-1][i]=Allocation[a-1][i]-Request[i];
Need[a-1][i]=Need[a-1][i]+Request[i];
goto start;
}
}
printf("\n\n安全!可以分配给 P%d 进程!\n",a);
//输出分配成功后资源占有、需求和可利用情况
printf("\n资源占有情况:\n");
for(i=0;i<N;i++)
{
printf("P%d 个进程已经分配的资源:",i+1);
for(j=0;j<M;j++)
printf(" %d ",Allocation[i][j]);
printf("\n");
}
printf("\n资源需求情况:\n");
for(i=0;i<N;i++)
{
printf("P%d 个进程还需要的资源:",i+1);
for(j=0;j<M;j++)
printf(" %d ",Need[i][j]);
printf("\n");
}
printf("\n可以利用的资源数据:");
for(i=0;i<M;i++)
printf(" %d ",Available[i]);
printf("\n");
}
return 0;
}