所谓死锁,是指多个进程因竞争资源而造成的一种僵局,若无外力作用,这些进程都将永远不能再向前推进。
下面举个例子,进程P1已经占用了资源R1,进程P2已经占用了资源R2,而P1和P2都要同时使用两个资源才能继续运行,所以P1要求R2,P2要求R1,这时候P1、P2便处于死锁状态。
产生死锁的两个原因:
1. 互斥条件:一个资源每次只给一个进程使用;
2. 请求保持条件:在申请新资源时,保持原资源;
3. 非剥夺条件:资源由占有者自愿释放,不得强制占用;
4. 循环等待条件:进程间形成了等待资源的环路。
不让死锁发生:
死锁可能发生:
预防死锁就要破坏死锁四个必要条件的一个或多个,但是其中第一个互斥条件是不能破坏的,因为资源可共享的情况下不可能发生死锁,也就没有预防死锁的前提了。
两种方案:
两个缺点:
方案1:申请资源得不到满足,释放已占有的全部资源;
方案2:请求资源得不到满足,讨论两种情况:
把系统中所有资源进行编号,进程在申请资源时必按资源编号的递增次序进行,否则系统不予分配,例如:
系统运行过程中,允许进程动态地申请资源,在资源分配之前,先计算此次资源分配的安全性,若分配导致系统进入不安全状态,则将资源分配给进程,否则令进程等待。
最有代表性的避免死锁算法是银行家算法。
安排好进程的顺序,使得按顺序可以顺利完成所有进程,就称系统处于安全状态,如果系统中的进程不存在这么一个安全序列,则称系统处于不安全状态。
要注意,安全状态一定是没有死锁发生的。
三个进程一类资源安全性检测代码:
#include
using namespace std;
int seq[100]; // 保存安全序列的下标
struct { // 代表三个进程的结构体
int max_need; // 最大需求
int allocated; // 已分配的资源数
int need; // 还需要的资源数
int state = 0; // 进程是否已运行,0代表未运行,1代表已运行
}P[3];
int main() {
for (int i = 1; i <= 3; i++) {
cin >> P[i].max_need >> P[i].allocated >> P[i].need;
} // 输入三个进程的数据
int available, work; // available表示目前可用的资源数,work用来代替available参与判断
int s = 3, j = 1; // s表示进程数,j是用来保存安全序列的下标
cin >> available;
work = available;
while (s--) {
for (int i = 1; i <= 3; i++) {
if (P[i].need <= work && P[i].state == 0) {
work += P[i].allocated;
seq[j++] = i;
P[i].state = 1;
}
}
}
if (seq[3] != 0) {
cout << " The system is safe because there is a safe sequence: ";
cout << "P" << seq[1] << "→P" << seq[2] << "→P" << seq[3];
}
else
cout << "The system is not safe!";
return 0;
}
当进程Pi提出资源申请,系统执行以下四个步骤:
1)若Request[i]≤Need[i],转2);否则错误返回(需要资源数超过最大值)
2)若Request[i]≤Available, 转(3);否则进程等待(无足够资源)
3)系统试分配资源,则有 3 个计算:
Available:=Available - Request[i];
Allocation[i]:=Allocation[i] + Request[i];
Need[i]:=Need[i] – Request[i]
4)执行安全性算法;若新状态安全,则分配完成,若新状态不安全,则恢复原状态,进程等待
为进行安全性检查,再定义2个数据结构:
Work: ARRAY[1..m] of integer;
Finish: ARRAY[1..n] of Boolean;
算法4个步骤如下:
1) Work := Available; Finish := false;
2) 寻找满足条件的 i : Finish[i] = false and Need[i]≤Work; 如果不存在,则转4)
3) Work:=Work+Allocation[i];Finish[i]:=true;转2)
4) 若对所有i,Finish[i]=true,则系统处于安全状态,否则处于不安全状态
#include
#include
#define PN 5//进程数
#define RN 4//资源种类数
typedef struct//定义结构体类型pcb
{
char name[3];//进程名,如p0
int max[RN];//最大资源值
int allocation[RN];//已分配资源数
int need[RN];//任需求资源数
}pcb;
struct//定义为结构体类型,方便赋值
{
int flag[PN];//进程检测标志,1时表示已通过
int safes[PN];//存放进程编号,作为安全序列输出
}fs0,fs;//fs0保存初始值,fs用于工作
struct//定义为结构体类型,方便赋值
{
int available[RN];//系统可用资源向量
}av0,av,avx;//av0保存初始值,av和avx用于工作
pcb proc0[PN],proc[PN];//proc0保存初始值,proc用于工作
int request[RN];//进程请求资源向量
void init();//初始化,输入数据
void output(pcb*);//输出数据
void cur_state();//判断系统当前状态
void req_state();//分析进程提出的请求是否可以响应
void back0();//退回至初始值
void back1(int);//退回至分配前
int check(int*);//进程need向量与available向量比较
int banker();//银行家算法
int main()
{
int num;
printf("【银行家算法】\n");
printf("进程数:%d\n",PN);
printf("资源数:%d\n",RN);
printf("=================\n");
init();
output(proc0);//输出初始资源分配情况
printf("【系统当前状态分析】\n");
cur_state();
printf("======================================================\n\n");
printf("选择操作序号(0:结束程序;1:资源请求分析)\n");
printf("输入序号:");
scanf("%d",&num);getchar();
while(1)
{
switch(num)
{
case 1:printf("\n【进程资源请求分析】\n");req_state();break;
default:printf("\n======分析结束!======\n");return 0;
}
printf("\n");
printf("======================================================\n");
printf("\n");
printf("选择操作序号(0:结束程序;1:资源请求分析)\n");
printf("输入序号:");
scanf("%d",&num);getchar();
}
}
void init()
{//初始化
int i,j;
for(i=0;iav.available[i])return 0;//若检测不通过则返回0
return 1;//检测通过
}
void cur_state()
{//检测系统当前状态
int i;
if(banker())
{
printf("分析结果:当前系统处于安全状态,存在安全序列 ");
for(i=0;iproc[n].need[i]||request[i]>av.available[i])
{
printf("分析结果:请求不合法,系统不予响应!");
for(j=0;j
区别死锁的避免和非避免:
示例如下:
答案:B
解析:如果只有两个进程的话,那么每个进程刚好能分配到4台打印机,不会死锁;但如果有三个进程的话,就有可能出现打印机分配为3、3、2的情况,此时每个进程都不满足,都再申请,但是都没有资源了,所以就会发生死锁,三个进程都这样,更不用说四个、五个进程了。所以K最小为3,选B。
答案:C
解析: 使得每个进程都只再申请一个资源时就能满足,在这里的已分配资源就是2、3、3,如果再加一个资源,系统就不会发生死锁,因为再加一个的话就可以满足其中的一个进程,等到这个进程结束后就会释放它的资源,从而满足剩下的进程。因此,可确保系统不发生死锁的设备数最小为9,选C。