Fork me on GitHub

Linux下死锁的产生,预防,避免,检测及解除

死锁的定义

1
所谓死锁就是指在多道程序系统中,一组进程中的每个进程都无期限的等待被该组进程中的另一个进程所占有且永远不会被释放的资源,这种现象称系统处于死锁状态,简称死锁。处于死锁状态的进程成为死锁进程。 如上面的图。

  系统发生死锁会大量浪费系统资源甚至会导致整个系统崩溃

死锁的产生的原因

产生死锁的原因主要有两个:一是竞争资源,系统提供的资源有限,不能满足每个进程的需求;二是多道程序运行时,进程的推进顺序不合理。
这里的资源我们作如下解释
系统资源分为两类:永久性资源(可重生资源),是指那些可供进程重复利用,长期存在的资源,如内存,CPU等硬件资源,以及数据集文件,共享程序代码等软件资源。临时性资源(消耗性资源),是指由某个进程产生,只为另一个进程使用一次,或经过短暂时间后便不可再使用资源,如I/O和时钟中断,消息等。
两种资源都可能导致死锁。

死锁的产生必要条件

对于永久性资源,产生死锁有以下四个必要条件:
1.互斥条件:进程独占所分配的资源且排斥其他进程使用。进程互斥使用资源,即任意时刻一个进程被进程使用,其他进程申请一个正在被占有的资源时,申请者要等待直至资源被占有者释放。
2.不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行剥夺,只能由使用者自愿释放。
3.请求与保持:进程已经得到至少一个资源时,又提出新的资源请求,而该资源又被其他进程所占有,此时进程会等待直至得到所需资源,在等待期间会继续占有已经得到的资源。
4.循环等待条件:在发生死锁时,必然存在一个进程等待队列p1,p2,p3,…,pn,其中p1等待p2占有的资源,p2等待p3占有的资源,…, pn等待p1占有的资源,形成一个进程等待环路。环路中每个进程已占有的资源同时被另一个进程所申请,即前一个进程占有后一个进程所申请的资源。

解决死锁的方法

解决死锁方法可分为两中:一是不让死锁发生;二是等死锁发生后再解决,具体有以下四种方法

死锁预防

通过破坏产生死锁的必要条件(除第一个互斥条件外的其他条件)来防止死锁发生。此方法会导致系统资源利用率过低。

1.破坏“不可剥夺”条件
在允许进程动态申请资源的前提下做出如下规定:一个进程在申请新资源的要求不能立即得到满足,该进程进入等待状态。而处于等待状态下的进程的全部资源可以被他人剥夺,被剥夺的资源重新放到资源表中。

  该方法适合那些状态是容易保存和恢复的资源,例如,CPU、内存等。但此方法实现起来较为复杂,且代价很大。因为一个资源在使用一段时间后被强制剥夺会造成前阶段工作失效,甚至可能出现某个进程反复申请和释放资源的情况,使得进程执行无限期推迟,还增加了系统开销,延长了进程的周转时间,降低了系统的吞吐量和性能。

2.破坏“请求和保持”条件
实现这一操作可以用两种方法。
方法一:每个进程在执行前必须申请它所需的全部资源,仅当系统能满足进程的资源申请并把资源一次性分配给进程后,进程才能执行。这是静态资源分配策略。这种方法的缺点是会严重浪费系统资源,降低资源利用率。

方法二:仅当进程没有占有资源时才允许他去申请资源,如果进程已占用了某些资源而又要申请新的资源,那他必须先归还所占有的资源再申请新的资源。

3.破坏“循环等待”条件
 采用资源有序分配策略,基本思想是将系统中所有的资源顺序编号,一般原则是紧缺、稀有的资源编号较大。进程申请资源时,必须严格按照编号顺序进行,否则不予分配。即一个进程只有得到编号较小的资源后,才能申请编号较大的资源。释放资源时应先释放编号较大的资源。
  此方法硬性规定申请资源,会给用户编程带来限制,增加了资源使用者的不便;此外如何合理的编号也是一件让人头疼的事。如有进程违反了规定也会造成死锁。

避免死锁

上述几种死锁预防策略增加了较强的限制条件,从而实现较为简单,但严重影响了系统性能,所以在实际应用中这几种方法使用少之又少,更好地方法是死锁避免。

  死锁避免的基本思想:系统对进程发出的每个系统能满足的资源申请进行动态检测,并根据检查结果决定是否分配资源;如果分配后系统可能发生死锁,则不予分配,反之予以分配。

  1. 安全与不安全状态
      由于避免死锁策略中允许进程动态地申请资源,所以系统要提供某种方法,在分配资源前,先分析资源分配的安全性。当估计到可能有死锁发生时及时设法避免。
      如果操作系统能保证所有进程能在有限时间内获得需要的全部资源,则称系统处于“安全状态”,否则就是不安全的。
      所谓的安全状态是指,如果系统的所有进程构成了一个安全序列,则系统处于安全状态。那么什么又是安全序列呢?如果一个进程序列:p1,p2,…,pnp1,p2,…,pn,对于其中每个进程 pi(1<=i<=n)pi(1<=i<=n),它以后尚需的资源总量不超过系统当前剩余资源量与所有进程当前占有资源总量之和,那么这个进程序列便称为安全序列。

2.银行家算法
  银行家算法是最经典的死锁避免算法之一。把系统比作银行家,系统资源比作周转资金,申请资源的进程比作向银行家带款的客户。那么银行家就要保证两件事:一是银行家能贷款给若干客户,满足客户对资金的需求;二是银行家可以安全地收回其全部贷款而不至于破产。为此,银行家做出如下规定:

当一个客户对资金的最大需求量不超过银行家现有资金总量时才可以接纳该客户。

客户可以分期贷款,但贷款总量不能超过该客户对资金的最大需求量。

当银行家现有资金不能满足客户尚需的贷款额度时,对客户的贷款可延迟支付,但必须保证客户在有限的时间内得到贷款。

当客户得到所需的全部资金后,必须在有限的时间内归还所有资金。

检测死锁

上述的死锁避免和死锁预防都是以牺牲系统效率和浪费资源为代价,不符合我国传统勤俭节约的美德,我们要采取更好地方法——系统为进程分配资源时,不采取任何限制措施来保证系统不进入死锁状态,即允许死锁发生,但系统会不断监督进程的进展路径,判断死锁是否真的发生,一旦判断发生死锁,则采取专门的措施解决死锁,并以最小的代价使整个系统恢复正常,这就是死锁检测和解除。

  死锁检测的实质是确定是否存在“循环等待”条件,检测算法确定死锁发生并识别出与死锁有关的进程和资源。下面介绍一种检测机制:

为每个进程和资源指定唯一编号。
设置一张资源分配表,表中包含“资源号”和占有该资源的“进程号”两项。资源分配表记录了每个资源正在被那进程占有。
设置一张进程等待表,表中包含“进程号”和该进程所等待的“资源号”两项。
死锁检测算法:当任一进程申请一个已被其他进程占有的资源时,通过反复查找资源分配表和进程等待表 ,来确定该进程对这个资源的申请是否会导致环路,若是,便确定出现死锁。

解除死锁

要解除死锁就要剥夺资源,那就要考虑如下几个问题:

选择牺牲一个进程,即要剥夺哪个进程的哪些资源?

被牺牲的进程重新运行或回退到某一点继续运行。

如何保证不发生“饿死现象”,即如何保证不会总是剥夺同一个进程的资源,从而导致该进程处于“饥饿状态”。

“最小代价”,即最经济合算的算法,使得进程回退带来的开销最小。要考虑到如下几个因素:

  • 进程优先级
  • 进程运行了多久?距离完成任务还要多久?
  • 该进程使用的资源的种类和数目,这些资源剥夺难度如何?
  • 为完成该进程还需要多少资源?
    有多少进程要被撤销?
  • 该进程被重新启动的次数?

死锁解除法归为两大类。

  • 剥夺资源。
      使用挂起/激活机制挂起一些进程,剥夺他们的资源给死锁进程,用来解除死锁,等死锁解除后再激活被挂起的资源。剥夺的顺序以花费最小资源数为依据。每次剥夺后需再次调用死锁检测算法。被重新激活的挂起进程必须重新申请资源。为安全的释放资源,该进程必须返回到分配资源前的某一点。常用方法有:

还原算法,恢复计算结果和状态。
设置检查点,用来恢复分配前的状态。

  • 撤销进程。(简单粗暴)
      撤销死锁进程,把它们占有的资源分配给死锁进程,直至死锁结束。可以撤销所有死锁进程或逐个撤销死锁进程,每撤销就检测死锁是否存在,若不存在就停止撤销。

衡量撤销代价的标准:

  1. 被撤销进程的优先数
  2. 不同类型的进程的撤销代价
  3. 重启进程并运行到当前撤销点所需的代价

  由于衡量标准过多,为了简化,把系统的资源分成四个等级,对于不同级别下出现的死锁,采取不同的方法。

内部资源:由操作系统使用,PCB表等——利用资源编号预防死锁。

内存:由用户使用——采用抢占式预防。

作业资源:可分配的设备和文件——可采用死锁避免措施。

对换空间:每个用户作业在辅助存储器上的空间——采用预先分配方式。

本文标题:Linux下死锁的产生,预防,避免,检测及解除

文章作者:LiuXiaoKun

发布时间:2018年11月28日 - 21:11

最后更新:2018年11月28日 - 22:11

原始链接:https://LiuZiQiao.github.io/2018/11/28/Linux下的死锁/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%