关于死锁你想知道的

先导知识

  • 死锁(Deadlock): 指进程之间无休止地互相等待!
  • 饥饿(Starvation):指一个进程无休止地等待!
  • 活锁(livelock):指进程没有被阻塞,但由于某些条件不满足,导致一直重复尝试,失败……
  • 本文内容在这里插入图片描述
  • 死锁概念
    • 两个或多个进程因竞争共享资源而造成的一种僵局,若无外力作用,这些进程都将永远不能再向前推进。
    • 涉及到进程就称为死锁进程。
  • 关于死锁的一些结论
    • 参与死锁的进程最少是两个 ;
    • 参与死锁的进程至少有两个已经占有资源
    • 参与死锁的所有进程都在等待资源;
    • 参与死锁的进程是当前系统中所有进程的子集。
    • 如果死锁发生,会浪费大量系统资源,甚至导致系统崩溃

3.5产生死锁的原因和必要条件

3.5.1 产生死锁的原因

  • 产生死锁的原因可归结为如下两点:

1、竞争资源

  • 资源类型:
    • 可剥夺和不可剥夺性资源
      死锁发生:双方都拥有部分资源,同时在请求对方已占有的资源
      例:竞争非剥夺性资源
      在这里插入图片描述

    • 永久性资源和临时性资源 (可重用资源和消耗性资源)
      (consumable resource):可以动态生成和消耗,一般不限制数量。如信号、消息、缓冲区内的数据。
      死锁发生:双方都等待对方去生成资源,如次序:P1 P2
      在这里插入图片描述

2、进程间推进顺序非法。

  • 例1
    在这里插入图片描述
    但如果将进程P2改写为这样,则容易导致死锁
    将类似①→③→④→②、①→③→②→④、③→①→②→④、 ③→①→④→②这样的序列称为不安全序列
  • 进程通信中的推进顺序及临时资源引发的死锁
    • 进程通信使用的信件是一种临时性资源,如果对信件的发送和接收不加限制,可能引起死锁。
      在这里插入图片描述
  • 如图所示三个进程:
    • P1等待P3的信件S3来到后再向P2发送信件S1;
    • P2又要等待P1的信件S1来到后再向P3发送信件S2;
    • P3也要等待P2的信件S2来到后才能发出信件S3。
  • 这种情况下形成了循环等待,产生死锁。
  • 正确的进程推进顺序(先发送再索取
    • P1: Release(S1); Request(S3); ……
    • P2: Release(S2); Request(S1); ……
    • P3: Release(S3); Request(S2); ……
  • 死锁的进程推进顺序
    • P1: Request(S3); Release(S1);……
    • P2: Request(S1); Release(S2);……
    • P3: Request(S2); Release(S3);……

3.5.2 产生死锁的必要条件

  • 1.互斥条件
  • 2.请求并保持条件
  • 3.不剥夺条件
  • 4.环路等待条件

3.5.3 处理死锁的基本方法

在这里插入图片描述

  • 一、预防死锁——消除产生死锁的必要条件(静态+动态)
  • 二、避免死锁——分配资源时防止进入不安全状态(动态)
  • 三、检测死锁——不预防死锁,随时检测死锁(动态)
  • 四、解除死锁——出现死锁就解除(动态)

3.6预防与避免死锁的方法

3.6.1预防死锁

  • 对于必要条件1(互斥条件),因为它是由设备的固有条件所决定的,一般不能改变。
  • 可以使用硬软件结合的SPOOLing技术资源转换技术),将独占设备“改造”为共享设备。
  • 此处预防死锁的方法,是使其余三个条件之一不能成立,来预防死锁。
  • 下面分别对三种情况加以说明:

1、静态资源分配法

所有进程在开始运行之前,都必须一次性 的申请其在整个运行过程所需的全部资源

  • 优点:算法简单、易于实现且很安全。
  • 缺点资源浪费严重,进程延迟运行;
    可能造成“饥饿”。

2、摒弃“不剥夺”条件

进程申请不到新的资源则必须放弃已到手的所有资源

  • 特点:
    • 实现复杂,代价大,造成进程前、后工作不连贯甚至结果出现混乱
    • 反复申请、释放,导致进程周转时间加长系统开销加大吞吐量下降

3、摒弃“环路等待”条件

  • 有序资源分配法:
    • 系统将所有资源按类型进行线性排队,并赋予不同的序号。
    • 所有进程对资源的请求必须严格按照资源序号递增的次序提出。
      在这里插入图片描述
  • 优点:资源利用率和系统吞吐量都有较明显的改善。
  • 缺点:为资源编号限制新设备的增加;进程使用设备顺序与申请顺序相反;限制用户编程自由。

例一过河问题

在这里插入图片描述

  • 题目描述
    • 每个石块上最多容纳一个过河者,两个相临石块的间距恰好为一步。
    • 西岸人经过石块1,2,5,6,4,3到东岸,
    • 东岸人经过石块3,4,7,8,2,1到西岸。
    • 试分析可能发生的死锁情况,给出一个无死锁无饿死并行度高的算法,并用Wait/Signal操作实现!
  • 分析
    • 当两个方向各有1人、2人(同时走上87或56)或其中一方有3或4人(一方占上了2或4)时,可能发生死锁!若双方同时有3人以上踏上石块(相互等待24),肯定发生死锁!
  • 解决方法
    • 方法1:规定东西两岸人员不能同时过河!但可能导致饿死,同时也影响并行度
    • 方法2:根据资源的数量,限定同时过河的人数在5个以内; 对两岸竞争的1,2和3,4两对石块,采用有序分配法
//方法2
Semaphore  Smax=5;semaphore  S1,S2,S3,S4,S5,S6,S7,S8;(初值=1)
西过河进程
Wait(Smax);
Wait(S1);
//走到石块1;
Wait(S2);
//走到石块2;
Signal(S1);
Wait(S5);
//走到石块5;
Signal(S2);
Wait(S6);
//走到石块6;
Signal(S5);
Wait(S3); 
//有序申请3-4
Wait(S4);
//走到石块4;
Signal(S6);
//走到石块3;
Signal(S4);//
走到东岸;
Signal(S3);
Signal(Smax);

东过河进程
Wait(Smax);
Wait(S3);
//走到石块3;
Wait(S4);
//走到石块4;
Signal(S3);
Wait(S7);
//走到石块7;
Signal(S4);
Wait(S8);
//走到石块8;
Signal(S7);
Wait(S1); 
//有序申请
Wait(S2);
//走到石块2;
Signal(S8);
//走到石块1;
Signal(S2);//
走到西岸;
Signal(S1);
Signal(Smax);

例2:T型路口

  • 问题描述
    • 设有一个T型路口,其中A、B、C、D处各可容纳一辆车,车行方向如下图所示,试找出死锁并用有序资源分配法消除之。要求资源编号合理。
      在这里插入图片描述
  • 死锁状态:
    • (1)E方向两辆车分别位于A和B;S方向一辆车位于C;W方向一辆车位于D。
    • (2)S方向两辆车分别位于B和C;E方向一辆车位于A;W方向一辆车位于D。
  • 设位置资源C、B、A、D的编号从低到高依次为1、2、3、4,管理四个位置的信号量分别为s1,s2,s3,s4,信号量的初值均为1。车辆活动如下
  • 解答
    在这里插入图片描述

3.6.2系统安全状态

  • 在预防死锁的几种方法中,都施加了较强的限制条件;在避免死锁的方法中,所施加的限制条件较弱,有可能获得令人满意的系统性能。
  • 在该方法中把系统的状态分为安全状态和不安全状态,只要能使系统始终都处于安全状态,便可避免发生死锁

1、安全状态

在这里插入图片描述
系统处于安全状态:存在安全进程序列<p1,p2,…,pn>,
按照这样的序列,p1,p2,…,pn可依次进行完。

2、安全状态例

  • 假定系统中有三个进程A、B和C,共有12台磁带机
  • 进程A总共要求10台,B和C分别要求4台和9台
  • 假设在T0时刻,进程A、B和C已分别获得5台、2台和2台,尚有3台未分配,如下表所示:
    在这里插入图片描述
  • 经分析发现,在T0时刻系统是安全的,因为此时存在一个安全序列<B,A,C>;
  • 即只要系统按此进程序列分配资源,就能使每个进程都顺利完成。

3、由安全状态向不安全状态的转换

  • 如果不按照安全序列分配资源,则系统可能会由安全状态进入不安全状态。
  • 例如,在T0时刻后,C又请求一台磁带机,若此时系统把剩余3台中的1台分配给C,则系统便进入不安全状态。
  • 因为,此时再也无法找到一个安全序列,可能导致死锁

3.6.3利用银行家算法避免死锁

1、银行家算法所需的数据结构

(1)可利用资源向量Available

可利用资源向量Available。这是一个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态地改变。如果Available[j]=K,则表示系统中现有Rj类资源K个

(2)最大需求矩阵Max

最大需求矩阵Max。这是一个n×m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源最大需求。如果Max[i,j]=K,则表示进程i需要Rj类资源的最大数目为K。

(3)分配矩阵Allocation

分配矩阵Allocation。这也是一个n×m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。

(4)需求矩阵Need

需求矩阵Need。这也是一个n×m的矩阵,
用以表示每一个进程尚需的各类资源数。
如果Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成其任务。
Need[i,j]=Max[i,j]-Allocation[i,j]

2、银行家算法

设Requesti是进程Pi的请求向量,如果Requesti[j]=K,表示进程Pi需要K个Rj类型的资源。当Pi发出资源请求后,系统按下述步骤进行检查:

(1)检查请求数是否超越动态的需求量

如果Requesti[j]≤Need[i,j],便转向步骤2;否则认为出错,因为它所请求的资源数已超过它所宣布的最大值;

(2)检查请求数是否超越可利用量(余量)

如果Requesti[j]≤Available[j],便转向步骤(3);否则, 表示尚无足够资源,Pi须等待。

(3)试探分配

系统试探着把资源分配给进程Pi,并修改数据结构中的数值:
Available[j]=Available[j]-Requesti[j];
Allocation[i,j]=Allocation[i,j]+Requesti[j];
Need[i,j]=Need[i,j]-Requesti[j];

(4)执行安全性算法

系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配;否则, 将本次的试探分配作废恢复原来的资源分配状态,让进程Pi等待

银行家算法流程

在这里插入图片描述

3、安全性算法

(1)设置两个向量

  • 工作向量Work: 它表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时Work∶=Available;
  • Finish: 它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先令Finish[i]∶=false; 当有足够资源分配给进程时,再令Finish[i]∶=true。

(2)检查所有进程目前的安全性

  • (2)从进程集合中找到一个能满足下述条件的进程:
    ①Finish[i]=false; ②Need[i,j]≤Work[j];
    若找到,执行步骤(3),否则,执行步骤(4)。
  • (3)当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
    Work[j]:=Work[j]+Allocation[i,j];//算出pi释放后的workj
    Finish[i]:=true;
    go to step 2;
  • (4)如果所有进程的Finish[i]=true都满足, 则表示系统处于安全状态;否则,系统处于不安全状态。

安全性算法流程

4、银行家算法之例

  • 假定系统中有五个进程{P0, P1, P2, P3, P4}和三类资源{A, B, C},各种资源的数量分别为10、5、7,在T0时刻的资源分配情况如图所示。
    在这里插入图片描述

  • 找出一个安全序列<p1,p3,p4,p0,p2>

  • 计算T0时刻执行安全性算法
    在这里插入图片描述

  • (2) P1请求资源:P1发出请求向量Request1(1,0,2),系统按银行家算法进行检查:

    • ① Request1(1, 0, 2)≤Need1(1, 2, 2)

    • ② Request1(1, 0, 2)≤Available1(3, 3, 2)

    • ③ 系统先假定可为P1分配资源,并修改Available, Allocation1和Need1向量,由此形成的资源变化情况如中的红色字体所示。
      在这里插入图片描述

    • ④ 再利用安全性算法检查此时系统是否安全。

    在这里插入图片描述

  • (3)随后P3,P4,P0,P2陆续提出资源请求,系统再分配前执行银行家算法和安全性算法

死锁避免优缺点

  • 优点:
    • 无须抢占;
    • 比死锁预防限制少;
    • 允许更多的进程并发执行。
  • 缺点:
    • 要预先声明资源的最大需求量;
    • 资源和进程的数目必须固定;
    • 可能导致进程的长时间阻塞。

死锁与饿死的不同

  • (1) 从进程状态考虑,死锁进程都处于等待状态,而处于就绪状态的进程也可能被饿死;
  • (2) 死锁进程等待永远不会被释放的资源,饿死进程等待会被释放但却不会分配给自己的资源;
  • (3) 死锁一定发生了循环等待,而饿死则不然。这也表明通过资源分配图可以检测死锁存在与否,但却不能检测是否有进程饿死
  • (4) 死锁一定涉及多个进程,而饥饿或被饿死的进程可能只有一个。

3.7死锁的检测与解除

3.7.1死锁的检测

  • 当系统为进程分配资源时,若未采取任何限制性措施,则系统必须提供检测和解除死锁的手段,为此系统必须:
    • 保存有关资源的请求和分配信息;
    • 提供一种算法,以利用这些信息来检测系统是否已进入死锁状态。
  • 死锁检测:
    • 允许死锁发生,操作系统不断监视系统进展情况,判断死锁是否发生
    • 一旦死锁发生则采取专门的措施,解除死锁并以最小的代价恢复操作系统运行
  • 检测时机:
    • 当进程等待时检测死锁
    • 定时检测
    • 系统资源利用率下降时检测死锁

当每类资源只有一个资源

  • 死锁检测算法:
    • 每个进程和资源指定唯一编号
    • 设置一张资源分配表
      记录各进程与其占用资源之间的关系
    • 设置一张进程等待表
      记录各进程与要申请资源之间的关系
  • 反复检测这两张表,列出所有等待与分配的关系,若出现循环等待,则出现了死锁!

每类资源含有多个资源的死锁检测-资源分配图

  • 定义: G=(V,E), V=P∪R, P={p1,p2,…,pn}, R={r1,r2,…,rm},
    E={(pi,rj)}∪{(rj,pi)}, piP, rjR.
    申请边(pi,rj): pi申请rj;
    分配边(rj,pi): rj分配pi;

  • 申请边:由进程到资源类;
    分配边:由资源实例到进程。
    在这里插入图片描述

  • 申请:pi申请rj中的一个资源实例,由pi向rj画一申请边,如可满足,改为分配边。

  • 释放:去掉分配边。

  • 例:P={p1,p2,p3}, R={r1(1),r2(2),r3(1),r4(3)} E={(p1,r1),(p2,r3),(r1,p2),(r2,p1),(r2,p2),(r3,p3)}

在这里插入图片描述
无环路,无死锁
增加边(p3,r2)
在这里插入图片描述

  • 例2
    在这里插入图片描述
    有环路,无死锁!(环路经过的是一类资源的不同子资源故无死锁)
  • 无环路,无死锁!
    有环路,可能死锁!

资源分配图的简化

  • 1.在资源分配图中找出一个既不阻塞又非独立的进程结点Pi,在顺利的情况下运行完毕,释放其占有的全部资源。
  • 2.由于释放了资源,这样能使其它被阻塞的进程获得资源继续运行。
  • 3.在经过一系列简化后若能消去图中的所有的边,使所有进程结点都孤立,则称该图是可完全简化的,反之是不可完全简化的

死锁定理

S状态为死锁状态的充分必要条件是当且仅当S状态的资源分配图是不可完全简化的


  • 在这里插入图片描述
    初始状态,红色虚线为申请边,蓝色实线为分配边
    在这里插入图片描述
    p1首先运行完毕释放资源

在这里插入图片描述

p2运行完毕并释放资源,此时p3,p4出现了循环等待,无法再释放资源,满足死锁定理出现死锁。

3.7.2死锁的解除

当发现进程死锁时,便应立即把它们从死锁状态中解脱出来。常采用的方法是:

  • 重新启动:简单但代价高!
  • 剥夺资源:从其他进程剥夺足够数量的资源给死锁进程以解除死锁状态。
  • 撤销进程:最简单的是让全部进程都死掉;温和一点的是按照某种顺序逐个撤销进程,直至有足够的资源可用,使死锁状态消除为止。
  • 进程回退:貌似完善但开销惊人!

死锁处理方法比较

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43515378/article/details/109596504