操作系统-死锁

在计算机系统中有很多资源独占性的资源,在任何时候他们只能被一个进程使用。常见的由打印机,磁带以及系统内部表的表项。打印机同时让两个进程打印将导致打印结果出现混乱,两个进程同时使用同一文件系统的表项会引起文件系统的瘫痪。正因如此,操作系统都授予进程排他的访问某一种资源的能力。两个进程因为请求资源而被阻塞,并一直处于这种状态,这种状况成为死锁。

  • 资源

大部分死锁都和资源有关,因此我们先了解关于资源的知识。当进程对设备、文件等取得排他性访问权时,就有可能会出现死锁。为了尽可能使关于死锁的讨论通用,我们把这类需要排他性的对象称之为资源(resource)。资源可以是硬件设备(如打印机)或是一组信息(如数据库的一条记录)。计算机通常会包含多类资源,一些类型的资源可能会有若干个相同的实例,例如有三台蓝光打印机。当一个类型的资源有多个实例时,任何一个实例都能够满足对于该种类型的资源的请求。简单的说资源就是随着时间推移,必须能获得、使用和释放的东西。

  • 可抢占资源和不可抢占资源

资源通常可分为两类:可抢占资源和不可抢占资源。可抢占资源(preemptable resource):可以从拥有它的进程中抢占并且不会产生任何副作用,存储器就是一类可抢占资源。不可抢占资源(nonpreemptable resource):是指在不引起相关计算失败的情况下,无法把资源从拥有它的进程中强占过来。例如打印机正在为一个进程打印文件,如果把打印机抢占过来并且开始打印工作,最后打印的结果上会出现两个进程的交叉。使用资源的所需的时间顺序可抽象为如下形式:

  1. 请求资源
  2. 使用资源
  3. 释放资源

若请求的资源不可用,则请求进程需要等待。一些操作系统中,资源请求失败的进程会自动被阻塞,在资源可用的时候再唤醒等待进程。在另外一些系统中,资源请求失败会返回一个错误代码,请求的进程会等待一段时间后再重试。

  • 死锁简介

死锁的规范定义:如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进程集合就是死锁的。产生死锁的条件:

  1. 互斥条件。一个资源要么已经分配给了一个进程,要么就是可用的。
  2. 占有和等待条件。已经获得资源的进程可以在请求新的资源。
  3. 不可抢占条件。已经分配给进程的资源不能够被强制性的抢占,只能由占用它的进程显式地释放。
  4. 环路等待条件。死锁发生时,系统中一定存在两个或两个以上的进程组成的一条环路,该环路的每一个进程都在等待下一个进程所占有的资源。

       当发生死锁时,如上四个条件必定同时满足,如果有一个条件不满足,那么死锁就不成立。值得注意的时,每个条件都与系统的一种可选策略有关。一种资源能否同时分配给不同的进程?一个进程能否在拥有一个资源的同时再请求其他的资源?资源能否被强占?循环等待环路是否存在?接下来我们就从这四个条件出发来解决死锁问题。

  • 死锁建模

Holt(1972)指出如何使用有向图 建立以上四个条件的模型。在有向图中有两类节点:用圆形表示的进程,用方形表示的资源。从资源节点到进程节点的有向边代表该资源已经被请求、授权并被进程占用。由进程节点到资源节点的有向边代表进程正在请求该资源,并且该进程阻塞,处在等待该资源的状态。资源分配图可以作为一种分析工具,考察对一定的请求/释放的序列是否会引起死锁。只需要按照请求和释放的次序一步步进行,每一步之后再检查资源分配图中是否包含环路,如果包含环路,那么就有死锁,反之则不存在死锁。通常处理死锁的四种策略:

  1. 忽略该问题。也许你忽略了该问题,他也会忽略你。
  2. 检测死锁并恢复。允许死锁发生,并进行适时地检查,如果存在死锁就采取行动解决。
  3. 仔细对资源进行分配,动态的避免发生死锁。
  4. 通过破坏死锁的四个必要条件之一,防止死锁的发生。

下面我们将讨论这四种方法。

  • 鸵鸟算法

最简单的方法就是鸵鸟算法:把头埋进沙子里,假装什么都没有发生。

  • 死锁检测与死锁恢复

使用这种方式时,系统不会阻止死锁的发生,而是在死锁发生时设法发现死锁并想办法解决死锁。我们将考察集中死锁检测和死锁恢复的方法。下面分别讨论死锁检测和死锁恢复。

死锁检测

  • 每种类型资源一个的死锁检测

从简单的例子出发,我们假设每种类型的资源都只有一个实例。这样的系统中可能有打印机,扫描仪,绘图仪,磁带机,但是每种设备都不超过一台。我们可以通过前面提到的死锁建模的方式来构造资源分配图,并分析资源分配图中是否存在环路,如果存在环路就存在死锁,如果不存在环路就不存在死锁。环路中的进程称之为死锁进程。考虑下面的资源分配图,图中存在环路(图b),那么就存在死锁,环路中的进程就是死锁进程。

虽然通过观察资源分配图可以很容易的检测出资源分配图中是否存在环路来判断是否存在死锁,我们仍然需要用算法来进行死锁检测。死锁检测算法可以等价转换成检测资源分配图中是否包含环路的算法。如下给出一个简单的算法,该算法对有向图进行检测,并在有向图中确定有环路或者确定无环路时结束。算法使用了数据结构L(L是一些节点的集合),算法对已经检测过的边进行检测,避免重复检测。算法的步骤如下:

  1. 对图中的每个节点N,将N作为起点执行下面五个步骤。
  2. 将数据结构L初始化为空,并清除所有有向边的标记。
  3. 将当前节点添加到L的尾部,并检测当前节点在L中是否出现两次。如果是,那么该图包含一个环,算法结束。
  4. 从给定的节点开始,检测是否存在没有标记的从该节点出发的弧(有向边),如果存在执行步骤5,否则执行步骤6。
  5. 随机选择一条没有被标记的从该节点出发的弧(有向边),标记它。然后顺着该弧寻找一个新的节点,返回步骤3。
  6. 如果该节点是起始节点,那么表明该图不存在任何环,算法结束。否则意味着我们进入了死胡同,需要移走更多的节点,返回前一个节点(回溯的过程),即当前节点的上一个节点,将他作为新的当前节点,并转到步骤3。
  • 每种类型资源多个的死锁检测

前面提到的每种类型资源一个的死锁检测,每种资源只有一个实例是相对容易的。对于同一种资源存在多个实例的系统中,例如一个系统中有两个打印机,三台磁带机......就需要另一种方法来检测死锁。使用一种基于矩阵的算法来检测从P1到Pn这n个进程中是否存在死锁。我们先对这种算法需要使用的一些向量做出声明。

假设资源共有m种(1~m),向量E表示现有资源向量 (existing resource vector)。比如编号1表示磁带机,那么E1=3表示系统共有3台磁带机。向量A是可用资源向量 (available resource vector),那么Ai表示第i种资源目前可以使用的数量(未被分配的数量)。例如A1=1表示系统目前可以使用的磁带机数为1。设数组C表示当前分配矩阵 (current allocation martix),数组R表示资源请求矩阵 (request martix)。C的第i行表示进程Pi持有的每一种资源的个数,R的第i行表示进程Pi请求每种资源的数目。Cij表示进程i拥有资源i的数量,Rij表示进程i请求资源j的数量。四种数据结构E,A,C,R的一个关系是∑ Cij + Aj = Ej (i=1,2,3....n) 。换言之,对于每一种资源,系统分配出去的资源数量加上可分配的资源能得到系统的资源总数。

死锁检测算法是基于向量的比较的,我们定义向量A和向量B之间的关系为A≤B以表明向量A的每一个分量都小于或者等于向量B的对应的每个分量。从数学上讲就是Ai≤Bi(i=1,2,3......n)。每个进程起初都是没有被标记过的,算法开始后会对每个进程进行标记,进程被标记就说明进程能够执行,不会进入死锁。当算法结束后,任何没有被标记的进程都是死锁进程,该算法假设每个进程在退出以前都会不停地获取资源。死锁检测算法如下:

  1. 寻找一个没有被标记的进程Pi,对于它而言R矩阵的第i行(Ri)向量小于等于A。
  2. 如果找打这样一个进程,就将矩阵C的第i行加到向量A上,标记该进程,并转到步骤1。
  3. 如果没有找到这样的进程,那么算法终止。

算法的核心思想:寻找一个进程使得它对资源的请求能够在此时被系统满足,那么系统分配资源让进程运行,进程运行结束后就把分配给该进程的所有资源都收回到系统。如果所有进程都能够被标记那么就不存在死锁,反之存在死锁。

现在我们介绍了对于不同类型资源数量为一个和多个时如何检测死锁,但问题在于何时检测死锁?

一种方法是每当有进程请求资源时就进行死锁检测,这样能够尽早的发现死锁,但这种方法会占用昂贵的CPU时间。另一种方法是每隔时间间隔k进行一次死锁检测,或者当CPU的使用率下降到某一阀值时再进行死锁检测(此时CPU的负载较低)。

死锁恢复

前面我们讨论了如何检测系统中是否存在死锁,那么当系统中真的发生了死锁我们该如何应对呢?因此我们接下来会介绍一些方法来处理死锁,从而使得系统恢复正常的工作。

  • 利用抢占恢复

在某些情况下,可以将资源从它的拥有者那里暂时移动给另一个进程,这种方法很大程度上取决于资源是否是属于可抢占类型的资源,这种方法通常实现比较困难或者说不太可能。若选择挂起持有资源的进程,在很大从程度上取决于哪个进程拥有比较容易收回的资源。

  • 利用回滚恢复

系统设计人员可以周期性的对进程进行检查点检查(checkpointed)。进程检查点检查就是将一个进程的状态写入到文件以备以后重启,该检查点包含了存储映像、资源状态等信息。当进程执行时,将会有一系列检查掉点文件被积累起来。一旦检测发现存在死锁,那么就很容易发现缺少那些资源,为了进行恢复,需要从一个较早的检查点开始,这样拥有资源的进程会回滚到一个较早的时间点,在此时间点之前进程获得一些资源,在该检测点之后的所有工作将被丢弃。实际上是将进程恢复到一个较早的状态,使得进程还没有获取到所需的资源,同时把这些资源分配给其他进程执行来解决死锁。

  • 通过杀死进程恢复

最直接也是最简单的解决死锁的方式是直接杀死一个或者多个进程。一种方式是杀死死锁环路中的进程,如果可能,杀死一个进程后死锁就能被解除,更一般的可能需要杀死多个进程才能解除死锁。另一种方式是杀死死锁环以外的进程,这种方式需要保证杀死的进程包含死锁进程需要的资源。有可能的话,杀死一个能够从头开始重新执行并且不会因为被杀死而影响系统正常运转的进程。

  • 死锁的避免

在讨论死锁检测时,我们假设进程每次请求资源都是请求它所需要的全部资源。但在大多数的系统中,一次只请求一个资源。系统必须能够判断分配资源是否安全,并且在系统安全的前提下才进行资源分配。因此就需要一种算法能够做出正确的选择从而避免死锁的发生。下面讨论几种死锁避免的方法。

  • 安全状态和不安全状态

对于死锁避免算法也用到了在死锁检测时关于每种类型资源多个检测算法的相关信息,即向量A,E;矩阵R,C。如果没有死锁发生,并且所有进程突然提出对资源的最大需求,也仍然能够找到某一直调度次序使得所有的进程都运行完毕,那么这种状态就是安全状态。考虑如下的一个例子,三个进程对一种拥有10个实例的资源的请求。最初进程A拥有该实例3个,对该种资源的最大需求9个;进程B拥有该实例2个,对该种资源的最大需求4个;进程C拥有该实例2个,对该种资源的最大需求5个。剩余的可分配资源个数为3个。那么可以找到一个调度次序使得ABC三个进程的请求都能够执行完毕,那么(a)就是处于安全状态。

如果对于(a),进程A请求得到一个资源,那么此时对于资源的请求分配情况就会是如下所示。在(d)处,无论把资源分配给A还是C都不能使得进程执行完毕,因此ad由安全状态进入到了不安全状态。需要指出的是,不安全状态并不是死锁。安全状态和不安全状态的区别是:从安全状态出发,系统能够保证所有进程都执行完毕,而从不安全状态出发就没有这种保证。

  • 单个资源的银行家算法

Dijkstra(1965)提出了一种能够避免死锁的调度算法,称之为银行家算法(banker's algorithm)。算法要做的是判断如果满足对资源的请求后系统是否会进入不安全状态。如果是,则拒绝请求;如果分配资源后系统仍然处于安全状态,就给予分配。银行家算法就是对每一个请求进行检查,检查如果满足这一请求是否会达到安全状态。若是,该请求就被满足。否则,就推迟满足该请求。为了检查是否处于安全状态,银行家算法需考虑可分配的资源能否满足某一个进程请求。

  • 多个资源的银行家算法

多个资源的银行家算法和单个资源的银行家算法非常类似。接下来我们考虑状态检测算法。

  1. 查找矩阵R中是否存在一行使得对于每种资源的请求都小于等于向量A。如果不存在这样一行,那么系统就会处于死锁,因为任何进程都无法运行结束(假设进程会一直占有资源直到他们终止为止)。
  2. 假设矩阵R存在这样一样,假设为应的进程分配资源并让进程执行结束,标记进程为终止,然后把该进程所拥有的资源加到向量A上。
  3. 重复步骤1和2,或者直到所有进程都被标记为终止,其初始状态是安全的。或者所有进程的资源请求都得不到满足,系统处于死锁状态。

银行家算法是Dijkstra于1965年发表的。该算法虽然十分有意义但却缺乏实际价值。因为很少有进程能够在运行时知道需要资源的最大值。并且进程数量也不短变化,如有新用户登录或注销。原本可用的资源也可能突然变得不可用......因此实际上系统大多使用银行家算法的启发式来进行死锁避免。

  • 死锁的预防

在前面的学习中我们知道,死锁的避免是不可能实现的,因为需要事先知道进程需要多少资源。那么系统又是如何避免死锁的呢?通常我们只要保证产生死锁的四个必要条件中的一个不成立,那么死锁也就不会发生。

  • 破坏互斥条件:破坏互斥条件,即资源不被同一个进程独占,那么死锁肯定不会发生。
  • 破坏占有且等待条件:只要避免持有资源的进程再等待其他资源,那么系统就不会发生死锁。一种方法是一个进程一次请求所需要的全部资源,如果系统能够满足资源请求则将资源分配给进程。如果一个或多个资源正在使用则不予分配,进程等待。这个方法问题和死锁避免一样需要事先知道进程需要多少资源,并且资源利用率也不是最优的。另一种方法是要求一个进程请求资源时,要求进程占时释放其所拥有的全部资源,然后再尝试一次获取全部的资源。
  • 破坏不可抢占条件:该方法取决于资源是否是可抢占类型资源。
  • 破坏环路等待条件:介绍一种方法。对系统中的所有资源进行统一编号。现在的规则是进程可以在任何时候提出请求,但是请求只能按照资源编号的升序提出。

如下是对死锁的预防的方法的汇总。

条件处理方式
互斥使用类似于假脱机的技术
占有和等待在开始时请求所有资源
不可抢占抢占资源
环路等待对资源进行编号

小结

死锁是在任何操作系统中都存在的潜在问题。当一组进程中的每一个进程都因为等待改组进程中的其他进程所占有的资源而导致阻塞时,死锁就发生了。在这种情况下所有的进程都处于无限等待的状态。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值