什么是死锁
死锁指的是并发进程彼此互相等待对方所拥有的资源,并且这些并发进程在得到对方的资源之前不会释放自己所拥有的资源,若无外力作用,它们都将无法推进下去。这些永远在互相等待的进程称为死锁进程。
死锁产生的根本原因在于系统提供的资源个数少于并发进程所要求的该类资源数。
死锁产生的必要条件
(1)互斥条件:并发进程所要求和占有的资源是不能同时被两个以上进程使用或操作的,进程对他所需要的资源进行排他性控制。
(2)不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行剥夺,而且只能由获得该资源的进程自己释放。
(3)请求和保持条件:进程已经至少保持了一个资源,但又提出新的资源请求,而该资源已被其他进程占有,此时请求进程阻塞,但对自己已获得的资源保持不放。
(4)循环等待条件:存在一种进程资源等待链,链中每一个进程已获得资源同时被链中的下一个进程所请求。
只要有一个条件不满足,死锁就可解除。
死锁处理
死锁处理策略如下
(1)预防死锁:破坏死锁产生的四个必要条件之一。
(2)避免死锁:用某种方法防止系统进入不安全状态,从而避免死锁。
(3)死锁的检测和解除:允许死锁发生,操作系统会负责检测出死锁的发生,然后采取某种措施去解除死锁。
预防死锁
(1)破坏“互斥条件”,把互斥使用的资源改造为允许共享使用。
(2)破坏“不剥夺条件”,系统规定,进程是逐个地提出对资源地请求,当一个已经保持了某些资源地进程,提出新的请求不被满足时,则必须释放它已经保持地所有资源,待以后需要时再重新申请。
(3)破坏“请求与保持条件”,每个进程在运行之前,必须预先提出自己所要使用的全部资源,调度程序在该进程所需要的资源未满足之前,不让它们投入运行。
(4)破坏“循环等待条件”,系统将所有资源类型进行线性排队,并赋予不同的序号,所有进程对资源的请求必须按照资源序号递增的次序提出,这样在所形成的资源分配图中,不可能出现循环。
预防死锁的方法虽然易实现,但其所施加的限制条件往往太严格,因而可能导致系统资源利用率和吞吐量降低。
避免死锁
避免死锁的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。
银行家算法
安全序列:是指如果系统按照这种序列分配资源,那么每个进程都能顺利完成,只要找出一个安全序列,系统就是安全状态。
如果系统处于安全状态,就一定不会发生死锁,如果系统进入不安全状态,就可能发生死锁。
因此可以在资源分配前预先判断这次分配是否会导致系统进入不安全状态,以此来决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
银行家算法步骤:
(1)检查此次申请是否超过了之前声明的最大需求数;
(2)检查此时系统剩余的可用资源是否还满足这次请求;
(3)试探着分配,更改数据结构;
(4)用安全性算法检查此次分配是否会导致系统进入不安全状态,如果安全,则分配成立;否则试探性分配作废,系统恢复原状,进程等待。
安全性检查算法:
检查当前剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。
不断重复上述过程,看最终是否能让所有进程都进入安全序列
Java实现银行家算法
package banker;
import java.util.Scanner;
public class Bank {
public static int pcb_nums; //进程数量
public static int res_nums; //资源种类数量
public static int max[][]; //最大需求
public static int alloc[][]; //拥有资源数量
public static int need[][]; //还需要资源数量
public static int ava[]; //可用资源数量
public static int request[]; //本次申请的资源数量
public static int safe_seq[]; //安全序列数组
public static void bank_init() {
Scanner in = new Scanner(System.in);
System.out.println("请输入进程数量:");
pcb_nums = in.nextInt();
System.out.println("请输入资源数量:");
res_nums = in.nextInt();
safe_seq = new int[pcb_nums+1];
max = new int[pcb_nums][res_nums];
alloc = new int[pcb_nums][res_nums];
need = new int[pcb_nums][res_nums];
ava = new int[res_nums];
System.out.println("请输入最大需求资源:");
for(int i=0;i<pcb_nums;i++) {
for(int j=0;j<res_nums;j++) {
max[i][j] = in.nextInt();
}
}
System.out.println("请输入分配资源:");
for(int i=0;i<pcb_nums;i++) {
for(int j=0;j<res_nums;j++) {
alloc[i][j] = in.nextInt();
}
}
//计算需求矩阵
for(int i=0;i<pcb_nums;i++) {
for(int j=0;j<res_nums;j++) {
need[i][j] = max[i][j]-alloc[i][j];
}
}
System.out.println("请输入可用资源:");
for(int i=0;i<res_nums;i++) {
ava[i] = in.nextInt();
}
}
//比较进程m中元素全大于n中的元素
public static boolean compare(int m[],int n[]) {
for(int i=0;i<res_nums;i++) {
if(m[i]<n[i]) return false;
}
return true;
}
//安全性检查函数,检测是否存在安全序列
public static boolean safe() {
boolean[] finish = new boolean[pcb_nums];
int[] work = new int[res_nums];
int num = 0;
for(int i=0;i<res_nums;i++) {
work[i] = ava[i];
}
for(int i=0;i<pcb_nums;i++) {
if(num == pcb_nums) break;
for(int j=0;j<pcb_nums;j++) {
if(finish[j]) continue;
else {
if(compare(work,need[j])) {
finish[j] = true;
safe_seq[num] = j+1;
num++;
//释放进程资源
for(int k=0;k<res_nums;k++) {
work[k] = work[k]+alloc[j][k];
}
}
}
}
}
for(int i=0;i<pcb_nums;i++) {
if(!finish[i]) return false;
}
//输出安全序列
for(int i=0;i<pcb_nums;i++) {
System.out.println(safe_seq[i]+" ");
}
return true;
}
//申请进称后的安全检验性函数,n代表进程号
public static void resafe(int n) {
if(compare(ava,request) && compare(need[n-1],request)) {
for(int i=0;i<res_nums;i++) {
alloc[n-1][i] = alloc[n-1][i] + request[i];
need[n-1][i] = need[n-1][i] - request[i];
ava[i] = ava[i]-request[i];
}
if(safe()) {
System.out.println("允许进程"+n+"申请资源!");
}
else {
System.out.println("不允许进程"+n+"申请资源!");
for(int i=0;i<res_nums;i++) {
alloc[n-1][i] = alloc[n-1][i] - request[i];
need[n-1][i] = need[n-1][i] + request[i];
ava[i] = ava[i] + request[i];
}
}
}
else
System.out.println("申请资源量越界!");
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n;
bank_init();
if(safe()) {
System.out.println("存在安全序列,初始状态安全。");
}else
System.out.println("存在安全序列,初始状态不安全。");
System.out.println("请输入发出请求向量request的进程编号:");
n = in.nextInt();
request = new int[res_nums];
System.out.println("请输入发出请求向量request:");
for(int i=0;i<res_nums;i++)
request[i] = in.nextInt();
resafe(n);
}
}
运行结果:
死锁的检测和解除
检测:用资源分配图来保存资源的请求和分配信息,并利用这些信息,用检测算法检测系统是否进入死锁状态。
解除:一旦检测出死锁的发生,就应该立即解除死锁。
解除死锁的方法有:
(1)资源剥夺法:挂起某些死锁进程,并将它的资源分配给其他死锁进程,但是应防止被挂起的进程长时间得不到资源而饥饿。
(2)撤销进程法:强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这种方法虽然实现简单,但是代价高昂。
(3)进程回退法:让一个或多个死锁进程回退到足以避免死锁的地步,这需要系统记录进程的历史信息,设置还原点。
参考
https://blog.csdn.net/qq_44096670/article/details/119857779
如有侵权,请联系作者删除