关于死锁的知识点总结

描述

在多道程序环境中,多个进程可以竞争有限数量的资源。当进程申请资源时,如果没有可用资源,那么这个进程进入等待状态。有时,如果所申请的资源被其它等待进程占有,那么该进程可能再也无法改变状态。这种情况称为死锁。

一、系统模型

资源分为多种类型,每种类型有一定数量的实例。

  • 资源类型R1, R2, . . ., Rm, 如CPU, 内存空间, I/O 设备,文件
  • 每个资源类型Ri有Wi个实例

正常操作模式下,进程只按如下顺序使用资源:

  1. 申请:进程请求资源
  2. 使用:进程对资源进行操作
  3. 释放:进程释放资源

当一组进程中的每个进程都在等待一个事件的发生,而这一事件只能由这一组进程的另一进程引起,那么这组进程就处于死锁状态

二、死锁特征

1.必要条件

如果在一个系统如下四个条件同时成立,就会引起死锁:

  • 互斥(mutual excusive):至少有一个资源只能由一个进程占用(非共享)
  • 占用并等待:一个进程应占用至少一个资源,并请求/等待另一个资源
  • 非抢占:资源不能被抢占
  • 循环等待:有一组等待进程{P0, P1, …, Pn},P0等待的资源被P1所占用,P1等待的资源被P2 所占用,…,Pn–1等待的资源被Pn所占用,Pn等待的资源被P0所占用

2.资源分配图

通过系统资源分配图的有向图可以更精确的描述死锁。

资源分配图由一个节点集合V和一个边集合E组成。

1.节点集合V分为进程集合P和资源集合R

  • 进程节点集合:P = {P1, P2, …, Pn}
  • 资源节点集合:R = {R1, R2, …, Rm}

2 边集合E

  • Pi → Rj,表示进程Pi 已经申请使用资源类型Rj的一个实例,并等待这个资源
  • Rj → Pi,表示资源类型Rj的一个实例已经分配给进程Pi

程序

程序

上图一个成环是死锁,一个成环不是死锁

根据资源分配图的定义,可以证明:

  1. 如果分配图没有环,就没有死锁
  2. 如果分配图有环,就有可能发生死锁如果每个资源类型只有一个实例,就肯定会发生死锁
    如果每个资源类型有多个实例,就有可能处于死锁

三、死锁处理方法

一般来说,处理死锁问题有三种方法:

预防或避免(prevention or avoidance)

  • 预防死锁:确保⾄少⼀个必要条件不成⽴
  • 避免死锁:利⽤事先得到进程申请资源和使⽤资源的额外信息,判断每当发⽣资源请求时是否会发⽣死锁

发生死锁,检测并恢复。确定死锁是否确实发⽣, 并提供算法从死锁中恢复

忽视死锁问题,认为死锁不会发生

四、死锁预防

发生死锁有4个必要条件,所以只要确保至少一个必要条件不成立,就能预防死锁发生

1.互斥

通常不能通过否定互斥条件来预防死锁

可共享的资源不要求互斥访问,如只读⽂件;但不可共享的资源本身就是非共享的,必须要确保互斥,如打印机,一个互斥锁

2.占用并等待

为否定这一条件,应保证:当一个进程请求一个资源时,它不能占有其他资源。

实现的方法如下:

  1. 每个进程在执⾏前申请并获得所有资源
  2. 另一种是进程只有在不占⽤资源时, 才允许申请资源

注意:让互斥和占⽤并等待不成⽴、两种⽅法的缺点是资源利⽤率低和可能发⽣饥饿问题

3.非抢占

为了确保这一条件不成立,可以是使用如下协议:

如果一个进程占有资源并申请另一个不能分配的资源(也就是说,这个进程应该等待),那么其现已分配的资源可被抢占。

如果一个进程申请一些资源,那么首先检查它们是否可用。

  1. 如果可⽤,就分配。
  2. 如果不可⽤,检查这些资源是否已分配给其他等待额外资源的进程,如果是,那么就抢占。
  3. 如果不可⽤,且也没有被其他等待额外资源的进程占有,那么就等待

4.循环等待

确保其不成立地一个方法:对所有资源类型进行完全排序,且要求每个进程按递增顺序来申请资源。

假设资源类型集合R={R1,R2…Rn},为每个资源类型分配一个唯一的整数,形式地,定义一个函数F:R → N(N为自然数集合)

可采用如下协议预防死锁:

每个进程只能按递增顺序来申请资源,即一个进程开始可以申请任何数量的资源类型Ri的实例,之后,仅当F(Rj)>F(Ri)时,进程才能申请资源类型Rj的实例

五、死锁避免

采用上述死锁预防中的的方法来预防死锁有副作用:设备使用率低和系统吞吐率低。

避免死锁的另一种方法,需要额外信息,即如何申请资源。在获悉每个进程的请求和释放的完整顺序后,系统可以决定,在每次请求时是否应该等待以避免未来可能的死锁。

需要掌握的额外信息包括:

  1. 当前可用资源
  2. 已分配给每个进程的资源
  3. 每个进程将来要申请或释放的资源
  4. 每个进程可能申请的每种资源类型实例的需求

针对每次申请,系统在做决定时考虑现有可用资源、现已分配给每个进程的资源、每个进程将来申请和释放的资源来申请与释放资源

鉴于这些先验信息,可构造算法确保系统不会进入死锁状态。避免死锁是动态的方法,它根据进程申请资源的附加信息决定是否申请资源

1.安全状态

如果系统按一定顺序(存在一个顺序)为每个进程分配资源(不超过它的最大需求),且能避免死锁,那么系统状态就是安全的,如果没有,系统状态就是非安全的

避免死锁指的是确保系统不进入不安全状态

2.资源分配图法

适用于每个资源具有单个实例。

由上一节的资源分配图的变形可以避免死锁

除原来的申请边和分配边,引入了新的类型边叫需求边。需求边用虚线Pi - - > Rj,表示进程Pi可能在将来某个时刻申请资源Rj;当进程Pi申请资源Rj时,需求边变成申请边(虚线变成实线);当进程Pi释放资源Rj时,分配变成需求边

程序

算法规则:只有在将申请边变成分配边而不会导致资源分配图形成环时,才允许申请资源。

假设进程Pi申请资源Rj,对资源的申请,把申请边变成分配边后,如果没有环就允许申请,如有环就不允许申请

下图中如果将R2分配给P2,就会创建一个环,表示系统处于非安全状态。

程序

3.银行家算法

适用于每个资源具有多个实例

当一个新进程进入系统时,它应声明可能需要的每种类型资源实例的最大数量,当然这不能超过系统资源总和。当用户申请一组资源时,系统应确定这些资源的分配是否会使系统处于安全状态,如果会,就可以分配;否则进程应等待,直到某个进程释放了做够多的资源为止。

为实现这一算法,引入一些数据结构,这些数据结构都资源分配系统的状态进行了记录

n是进程数 m是资源类型

Available ( vector 向量): 表示可分配的资源数。Available[j] = k 表示资源Rj 的可用资源数是k个

Max(n x m 矩阵): 表示资源最大需求数。Max[i,j] = k 表示进程Pi可能请求的资源Rj的实例个数是k.

Allocation(n x m 矩阵): 表示占有的资源数。Allocation[i,j] = k 表示Pi已经占有的资源Rj.实例的个数是k.

Need(n x m 矩阵): 为完成任务可能仍然需要的资源。Need[i, j] = k, 表示进程Pi可能需要的资源Rj实例数是k.

安全性算法:确定计算机系统是否处于安全状态的算法。

//伪代码
//Work 和Finish 分别为长度m 和n的向量
Work = Available;//可用资源数

For all i, Finish[i] = false;//初始化,各个进程状态

//这里并不是简单的遍历一遍,而是不断查找
For all i do
//表示当前进程可以获得资源,并执行
IF ( Finish[i] == false && Need i<= Work)
Work = Work + Allocation i//释放进程i原来的占有资源
Finish[ i ] = true//表示执行完成

End IF
End For

//所有进程都执行完
IF for all i, Finish[i] == true
Then the system is safety
End IF

STEP 1:

Work 和Finish 分别为长度m 和n的向量, 分别初始化为:

Work = Available //可分配的资源数

Finish [ i ] = false for i = 0, 1, …, n- 1

STEP 2:

查找i使其满足:Finish [ i ] = false && Need i <= Work

如果没有满足以上条件的i, 那么就跳转到STEP 4

STEP 3:

Work = Work + Allocation i

Finish[ i ] = true

返回到STEP 2

STEP 4:

如果对所有i, Finish[i] == true 那么系统处于安全状态,如果不是就处于不安全状态

资源请求算法:判断是否可安全允许请求的算法。

每次进程请求资源的时候,运行资源请求检测算法,确认是否允许请求

//伪代码
IF ( Request i <= Need i) //检测资源的请求是否合法
IF (Request i <= Available i){ //检测可用资源能否满足请求
Available i = Available i– Request i ;
Allocation i = Allocation i + Request i ;
Need i = Need i– Request i ;

do Safety Check Algorithm; //检测分配资源后是否安全

ELSE
waiting;
End IF
ELSE
error
End IF

设Request i 为进程P i 的请求向量,当进程Pi作出资源请求时,采取如下操作:

STEP 1:

如果Requesti <= Need i , 那么转到STEP 2. 否则,产生出错条件,这是因为进程Pi 已超过了其最大请求

STEP 2:

如果Requesti <= Available, 那么转到STEP 3. 否则Pi必须等待,这是因为没有可用资源

STEP 3:

假定系统可以分配给进程Pi所请求的资源,则修改资源分配状态,进入安全性检查

  1. 如果所产生的资源分配状态是安全的,那么Pi 可分配到其所需要资源.
  2. 如果所产生的资源分配状态是不安全的,那么Pi 必须等待并恢复到原来资源分配状态

4.银行家算法实例

假设一个系统,有5个进程:P0、P1、P2、P3、P4。3 种资源类型: A (10个实例), B (5个实例),C (7个实例)。

假定在时间T0,系统资源分配状态如下:

程序

执行算法过程:

查找满足如下条件的i :Finish [ i ] = false && Needi <= Work

发现P1满足以上条件,则Finish[1] 设置为1,

Work = Work + Allocationi;

Work = 【3,3,2】+ 【2,0,0】

继续往下查找,

发现P3 满足条件,则Finish[3]设置为1,

Work = 【5,3,2】+ 【2,1,1】= 【7,4,3】;

继续往下查找,

发现P4 满足条件,则Finish[4]设置为1,

Work = 【7,4,3】+ 【0,0,2】= 【7,4,5】;

继续往下查找,

发现P0满足条件,则Finish[0]设置为1,

Work = 【7,4,5】+ 【0,1,0】= 【7,5,5】;

继续往下查找,

发现P2满足条件,则Finish[2]设置为1,

Work = 【7,5,5】+ 【3,0,2】= 【10,5,7】;

此时Finish都为true,说明分配资源后,系统仍出于安全状态,安全顺序为【P1,P3,P4,P0,P2】

对上述例子及银行家算法和安全状态的理解:

银行家算法算的是是否可以分配给某个进程某些资源,所以假设系统把资源分配后,去判断系统是否仍处于安全状态。上面例子T0时刻的资源状态,其实就是某个进程请求了某些资源后的一个状态,算法需要去判断这个状态是否安全,如果安全便可以分配。

上文提到了什么是安全状态,即在当前资源分配下,存在一个序列使所有进程运行不死锁。关键在于如何知道存在,银行家算法模拟了最坏情况,即进程每次都请求最大数量的Need(显然实际环境中进程可能只请求一个实例),如果这样分配资源仍然能使所有进程都完成,那显然这个序列就是存在的,系统就是安全的

六、死锁检测

如果一个系统既不采用死锁预防算法也不采用死锁避免算法,那死锁可能出现,这种环境下系统可以提供:

  1. 检测算法:确定系统是否进入死锁
  2. 恢复算法:从死锁状态中恢复

需要从如下两个方面分别考虑这个问题:

  1. 第一个方面,每个资源类型有单个实例
  2. 另一个方面,每个资源类型有多个实例

1.每种资源类型只有单个实例

检测算法:用等待图,它是资源分配图的一个变形,从资源分配图中删除所有资源类型节点,合并适当边,就能得到等待图。

  • 每个节点是进程
  • Pi → Pj 意味着进程Pi等待进程Pj释放一个Pi所需的资源

如等待图中有环,系统中存在死锁。

为检测死锁,系统需要维护等待图,并周期性的调用在图中进行搜索环的算法

程序

2.每种资源类型可有多个实例

类似于银行家算法

3.应用检测算法

何时调用算法取决于:

  1. 死锁发生的频率
  2. 死锁发生时,有多少进程会受影响

每次资源请求调用检测算法

每次资源请求不被允许时调用检测算法

七、死锁恢复

当检测算法确定已有死锁是,需要打破。打破死锁有两个选择,一个是简单的终止一个或多个进程来打破循环等待;另一个是从一个或多个进程那里抢占一个或多个资源

1.终止进程

两种方法:

  1. 终止所有死锁进程
  2. 一次只终止一个进程直到取消死锁循环为止

如果采用部分终止,我们应该终止造成最小代价的进程,许多因素影响了选择哪个进程:

  • 进程的优先级
  • 进程已计算了多久,进程在完成指定任务之前还需要多久
  • 进程使用了多少数量的何种类型的资源
  • 进程需要多少资源以完成
  • 多少资源需要被终止
  • 进程是交互的还是批处理的

2.资源抢占

通过抢占资源以取消死锁,逐步从进程中抢占资源给其他进程使用,直到死锁被打破为止。

需要处理三个问题

  1. 选择牺牲品:抢占哪些资源和哪个进程
  2. 需要考虑饥饿:避免同一个进程总成为牺牲品
  3. 回滚(Rollback):必须把不能正常运行的进程,回滚到某个安全状态,以便重启进程
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分