ch7-死锁

死锁概念及其资源分配图

死锁概念

  • 概念:在多道程序环境下,一组处于等待状态的进程,其中每一个进程都持有资源,并且等待着由这组中其他进程所持有的资源,那么该组等待进程有可能再也无法改变其状态,这种情况称为死锁
  • 所有的死锁进程如无外力的介入,都无法往前推进。
  • 引起死锁的主要原因:竞争互斥资源,进程推进不当。
    例:某系统有两个磁带设备,进程P1和P2各占有一个磁带设备,并且实际需要两个磁带设备,此时P1和P2均死锁。

死锁的必要条件(特征)

  • 互斥:至少有一个资源必须处于非共享模式,即一次只有一个进程使用。
  • 占有并等待:一个至少持有一个资源的进程等待获得额外的由其他进程所持有的资源。(并不是指所有的进程都满足,满足该条件的进程至少有2个即可
  • 非抢占:资源不能被抢占,即资源只有在持有它的进程完成任务后自动释放。
  • 循环等待:一组等待资源的进程{P0,P1,…,Pn}之间存在循环等待现象,即P0等待P1占有的资源,P1等待P2占有的资源,…,Pn-1等到Pn占有的资源,Pn等待P0占有的资源。
  • 如果上述四个条件同时出现,死锁将会发生

系统模型

  • 资源类型的例子:内存空间,CPU周期,文件,I/O设备等。
  • 形式化表示为:资源类型R1,R2,…,Rm,每个资源Ri有Wi种实例。
  • 进程使用资源的顺序:
    申请资源–>使用资源–>释放资源
    申请资源分为动态申请资源和静态申请资源,动态申请资源是在进程运行中申请资源。静态申请资源是在进程运行前一次性申请所有资源静态申请不考虑占有并等待问题。静态申请效率差,所以现代操作系统广泛使用动态申请资源

资源分配图

一个顶点的集合V和边的集合E

  • V被分为两个部分
    P={P1,P2,……,Pn}含有系统中全部活动的进程。
    R={R1,R2,……,Pm}含有系统中全部的资源。
  • 由进程Pi到资源Rj的有向边记为Pi–>Rj,表示进程Pi申请了资源Rj的一个实例,该边称为申请边
  • 由资源Rj到进程Pi的有向边记为Ri–>Pj,表示资源Rj的一个实例已经分配给进程Pi,该边称为分配边
    在这里插入图片描述
  • 如果分配图没有环,那么系统就没有进程死锁,因为它破坏了循环等待条件。
  • 如果分配图有环,那么可能存在死锁;若每一种资源类型只有一个实例,那么肯定有死锁发生。如果资源有多个实例,但每个实例都在环中时,就有死锁。如果不都在环中,那么可能会打破环或部分环。
  • 有环有死锁的资源分配图
    在这里插入图片描述
  • 有环但没死锁的资源分配图
    在这里插入图片描述

处理死锁的方法

  1. 可使用协议来预防或避免死锁,确保系统不会进入死锁状态。(系统可以采用死锁预防或死锁避免方案。)
  2. 允许系统进入死锁状态,然后检测它,并加以恢复。(系统可提供死锁检测算法,并提供死锁恢复算法从死锁中恢复,或称死锁解除。部分系统采用,尤其是进程量特别庞大的大型机系统中)
  3. 忽略这个问题,认为死锁不可能在系统内发生。(此方法为绝大多数操作系统所采用,包括UNIX,Windows,因此需要由应用程序的开发人员自己来处理死锁。)

  • 不采用第一种方式的根本原因是:每一个操作系统采用某项技术时,都会考虑做了之后,能得到什么,性能提高多少,有什么好处。如果采用死锁预防来设计操作系统,那么效率将会很低,即设计了很多,但得到的很少,这会造成资源的大量浪费,不合算。避免也是,浪费大量资源
    检测和恢复也是。少部分会用它,一些程序的代价可能比人工来得大,做恢复时,人有更好的策略,比机器做的更高效,而且什么样问题怎么处理,机器不知道,但人知道。
  • 第三种方式:这种方式也叫鸵鸟算法。第一个原因:死锁的发生是一个小概率事件,特别在现代操作系统中,即死锁造成损失很小。如果用上面方法做,付出很大,不合算。比如个人计算机几个小时就关掉了。死锁在计算机中5天出现一次,如果计算机一直不关,可能会出现。但关掉了,死锁就没有了。而且人发现慢了,卡了,可以重启电脑。服务器死锁,重启就行。第二个因素:在现代操作系统中,我们引入了虚拟化技术,利用这个技术,可以把一些互斥独占设备虚拟化成为一些共享设备。比如,一个打印机我们得互斥访问它,但可以把这个物理打印机虚拟成成千上万个虚拟打印机,一个进程申请时,分给它虚拟打印机,理论上数量是无线的,这样互斥设备就变成了共享设备。就打破了互斥条件,就没有死锁了。
  • 竞争互斥资源和进程推进不当会造成死锁,且第一种的可能性远远大于第二种。我们把互斥资源虚拟化以后成共享资源,那么进程推进不当就不会有了。这就是现代操作系统中死锁发生比较低的因素。如果进程推进不当。这个操作系统管不到,这是自己程序的问题,影响是局部的,由用户自己可以管控。通过忽略死锁来简化操作系统工作,另一方面降低系统开销,所以绝大部分忽略这个问题。操作系统忽视,但程序员不能忽视。

死锁预防

就像打疫苗,死锁预防可以保证系统中不发生死锁

  • 死锁的4个必要条件:互斥,占有并等待,非抢占,循环等待
  • 死锁预防(deadlock prevention)是一组方法,只要确保至少一个必要条件不成立,就能预防死锁
  • 操作:通过限制资源申请的方法来预防死锁。
  • 缺点:降低了设备使用率和系统吞吐量。

死锁的预防1

  • 共享资源:不涉及死锁
  • 非共享资源(互斥资源),必须要有互斥条件
  • 现代操作系统中的虚拟化技术,将互斥资源改造为共享资源;
  • 如系统中存在互斥资源,不能通过改变这个条件来预防死锁。

死锁的预防2

  • 占有并等待:必须保证进程申请资源的时候没有占有其他资源,这种方法是静态分配策略,即在设计操作系统的时候就规划好这样的申请资源方式。
  • 一种可用的协议是每个进程在执行前一次性申请并获得所有资源。另外一种协议则允许进程在没有资源时才可申请资源。进程可申请资源并使用它们,但在申请更多资源前必须释放当前资源
    在这里插入图片描述
    若使用第一种协议,则在进程开始前就申请DVD驱动器,磁盘文件和打印机,并在过程中一直占有打印机。第二种协议允许进程一开始只申请DVD驱动器和磁盘文件,在复制完成后,释放DVD和磁盘,然后再申请磁盘和打印机,当数据打印完后,再释放资源并终止。
  • 缺点:资源利用率低,一些资源可能已分配,但很长时间没有使用。可能发生饥饿,一个进程若需要多个常用资源,则可能永久等待,因为它要等待的资源中至少有一个已分配给其他进程。

死锁的预防3

  • 对于非抢占:如果一个进程的申请没有实现,它要释放所有占有的资源
  • 当一个进程处于等待时,如果其他进程申请其占有的资源,那么该进程的部分资源可以被抢占。一个进程要重新执行时,它必须分配到其所需资源,并恢复其在等待时被抢占的资源。
  • 这个协议通常应用在可以保存和恢复的资源,如CPU寄存器和内存等,一般不适用于其他资源,如打印机
    在这里插入图片描述

死锁的预防4

  • 对于循环等待:对所有的资源类型排序进行总排序,并且要求进程按照递增顺序申请资源。设R={R1,R2,…,Rm}为资源类型的集合,为每个资源类型分配一个唯一整数来允许比较两个资源以确定其顺序。为预防资源,每个进程只按递增顺序申请资源,即一个进程开始可以申请任何资源类型Ri的实例。当且仅当Rj的值大于Ri的值时,进程才可以申请Rj的实例。
  • 在这个协议中,设计一个完全排序方法并不能防止死锁,而要靠应用程序员来按照顺序编写程序。
  • 各资源类型的序号确定也是至关重要的,一般要按照系统内部资源使用的正常顺序定义。如磁盘一般先于打印机使用,则定义打印机的值要大于磁盘的值

死锁避免

  • 操作:获得以后如何申请资源的附加信息来决定是否分配资源。每次申请要求系统考虑现有可用资源,现已分配给每个进程的资源和每个进程将来申请与释放的资源,以决定申请是否满足,从而避免死锁发生的可能性。
  • 操作系统在设计时不能保证系统中不会出现死锁,即如果不加以干预,当前操作系统会出现死锁。但在具体的运行中我们可以引入一种机制,使得在这种机制的干预下,这种操作系统永远不会出现死锁。
  • 简单举例:每次有进程来申请资源时,都判别分配后会不会出现死锁,若会,则不分。若不会,就分
  • 最简单和有用的模型要求进程说明可能需要的每种资源类型实例的最大需求。基于此,建立一个死锁避免的算法,该算法动态检查资源分配状态以确保循环等待条件不可能成立。资源分配状态是由可用资源和已分配资源,及进程最大需求所决定的。
  • 通常不会采用死锁避免。1.如果采用死锁避免,在每次进程运行前都进行检测,死锁发生是低频事件,那么效率将会很低,即设计了很多,但得到的很少,这会造成资源的大量浪费,不合算;2.每个进程运行前要告诉系统需要每个资源的最大数来避免死锁,但很多时候是得不到的。一个进程在运行中有很多分支可走,比如人机交互的进程。在早期批处理系统中是可以的,但在现代操作系统中是很困难的;3.实际上死锁避免的算法判别到的资源的分配会导致死锁的情况中只有很小的可能发生死锁,即如果算法检查100次,判定有99次会发生死锁,但真正发生死锁可能只有1,2次,甚至不会有死锁。

安全状态

  • 安全状态:如果系统能按某个顺序为每个进程分配资源并能避免死锁,那么系统状态是安全的。即,如果存在一个安全序列,那么系统处于安全状态,否则系统处于不安全状态。绝对没有死锁发生的状态。
  • 安全序列:进程序列<P1,P2,…,Pn>是安全的,如果每一个进程Pi所申请的可以被满足的资源数加上其他进程所持有的该资源数小于系统总数。
    如果Pi需要的资源不能马上获得,那么Pi等待直到所有的 P i − 1 P_{i-1} Pi1进程结束。
    P i − 1 P_{i-1} Pi1结束后,Pi获得所需的资源,执行,返回资源,结束。
    当Pi结束后, P i + 1 P_{i+1} Pi+1获得所需的资源执行,依此类推。

安全状态与死锁状态之间的关系

安全状态不是死锁状态,相反,死锁状态是不安全状态。

  • 如果一个系统在安全状态,就没有死锁
  • 如果一个系统不是处于安全状态,就有可能死锁。
  • 避免–>确保系统永远不会进入不安全状态

采用这种方案,如果进程申请一个现已可用的资源,它有可能必须等待。因此,与没有采用死锁避免算法相比,这种情况下资源利用率可能更低

避免算法

  • 单实例资源:如果有一个系统,每种资源类型只有一个实例。可以通过修改前面所说的资源分配图来避免。
  • 多实例资源:对于每种资源类型有多个实例的系统,可采用银行家算法来避免死锁,但效率比资源分配图的方法要低。

资源分配图方法

在原资源分配图中,除了申请边和分配边外,引入一种新类型的边,称为需求边

  • 需求边 Pi–>Rj:Pi可能以后需要申请Ri资源,用虚线表示
  • Pi申请Rj资源,需求边转换为请求边
  • 请求边在资源分配后转换为分配边
  • 资源释放后,分配边转换为需求边
资源分配图算法

假设Pi申请资源Rj
请求能满足的前提是:把请求边转换为分配边后不会导致环存在。如果没有环存在,那么系统处于安全状态。如果有环存在,那么这种分配会导致系统处于不安全状态,因此该进程的资源申请必须等待。
在这里插入图片描述
若此时P2申请R2,R2分配给P2的话,那么就会构成一个环,系统处于不安全状态。
从算法效率上讲,环检测算法需要 n 2 n^2 n2级的操作,其中n是图中节点数,也就是系统中的进程数量。

银行家算法

  • 每一个进程必须事先申明使用的最大量,这一数量不能超过系统资源的总和。
  • 当一个进程请求资源,它可能要等待,因为系统必须确定这些资源分配是否会使系统处于安全状态。
  • 当一个进程得到所有的资源,它必须在有限的时间释放它们,这些资源可投入再分配。
银行家算法的数据结构

为了实现银行家算法,必须有几个数据结构,这些结构对系统的状态进行了记录,其中n为系统进程的个数,m为资源类型的种类

  • Available: 长度为m的向量。如果available[j]=k,那么资源Rj有k个实例有效。
  • Max:n x m矩阵。如果Max[i,j]=k,那么进程Pi最多可以请求k个资源Rj的实例。
  • Allocation : n x m矩阵。如果Allocation[i,j]=k,那么进程Pi当前分配了k个资源Rj的实例
  • Need: n x m矩阵。如果Need[i,j]=k,那么进程Pi还需要k个资源Rj的实例
    Need[i,j]=Max[i,j] - Allocation[i,j]

安全算法步骤

  1. 让Work和Finish作为长度为m和n的向量初始化:
    Work = Available
    Finish[i]=false for i=1,2,3,…,n
  2. 查找i
    (a) Finish[i] = false
    (b) Needi<=Work
    if not such i exists. go to step 4.
  3. Work = Work + A l l o c a t i o n i Allocation_i Allocationi
    Finish[i]=true
    go to step2.
  4. 如果对所有的i的Finish[i]=true,则系统处于安全状态。

这个算法需要 m ∗ n 2 m*n^2 mn2数量级的操作。


  • 判断是否满足进程Pi的资源请求算法:
    R e q u e s t i Request_i Requesti=进程Pi的资源请求向量。如果 R e q u e s t i [ m ] = k Request_i[m]=k Requesti[m]=k则进程 P i P_i Pi想要资源类型为 R m R_m Rm的k个实例
  1. 如果 R e q u e s t i < = N e e d i Request_i<=Need_i Requesti<=Needi转step2.否则报错,因为进程请求超出了其声明的最大值
  2. 如果 R e q u e s t i Request_i Requesti<=Available,转step3.否则 P i P_i Pi 必须等待,因为资源不可用。
  3. 假设通过修改下列状态来分配请求的资源给进程 P i P_i Pi
    A v a i l a b l e = A v a i l a b l e − R e q u e s t i Available=Available - Request_i Available=AvailableRequesti
    A l l o c a t i o n i = A l l o c a t i o n i + R e q u e s t i Allocation_i =Allocation_i+Request_i Allocationi=Allocationi+Requesti
    N e e d i = N e e d i − R e q u e s t i Need_i=Need_i-Request_i Needi=NeediRequesti

如果系统安全–>将资源分配给 P i P_i Pi
如果系统不安全–>Pi必须等待,恢复原有的资源分配状态

  • 银行家算法实例
    在这里插入图片描述
    在这里插入图片描述

死锁检测和恢复

  • 会有死锁发生,会有检测进程定期去查看进程内有无死锁,如果有,就调死锁恢复来解除死锁。系统应该提供一个用来检测系统是否出现了死锁的算法和一个用来从死锁状态中恢复的算法。
  • 分两类进行分析:每种资源类型只有单个实例;每种资源类型可有多个实例

死锁的检测

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

资源分配图-->维护等待图

  • 维护等待图,如上图的左图的等待图为右图。节点是进程, P i P_i Pi–> P j P_j Pj表明 P i P_i Pi在等待 P j P_j Pj,当且仅当相应的资源分配图中包含两条边 P i P_i Pi–> R q , R q R_q,R_q RqRq–> P j , R q P_j,R_q PjRq为资源。
  • 当且仅当等待图中有环,系统存在死锁;为了检测死锁,系统需要维护等待图并周期性地调用算法来检查是否有环
  • 一个检查图中是否有环的算法需要 n 2 n^2 n2的操作来进行,n为图中的节点数,即进程数
每种资源类型可有多个实例

等待图方案并不适用

  • 死锁检测算法
    Available:一个长度m的向量,表示每一种资源类型可用的实例数目。
    Allocation:一个n x m的矩阵。定义了当前分配的每一种资源类型的实例数目。
    Request:一个n x m的矩阵,表明了当前的进程请求。如果Request[i,j]=k,那么进程 P i P_i Pi请求k个资源 R j R_j Rj的实例。
检测算法的具体步骤
  1. 让Work和Finish作为长度为m和n的向量初始化
    ( a ) W o r k = A v a i l a b l e (a) Work = Available (a)Work=Available
    ( b ) F o r i = 0 , 1 , 2 , . . . , n − 1 , i f A l l o c a t i o n i ≠ 0 , t h e n F i n i s h [ i ] = f a l s e ; o t h e r w i s e , F i n i s h [ i ] = t r u e (b) For i =0,1,2,...,n-1,if Allocation_i\neq0,then Finish[i]=false;otherwise,Finish[i]=true (b)Fori=0,1,2,...,n1,ifAllocationi=0,thenFinish[i]=false;otherwise,Finish[i]=true
  2. 找到满足下列条件的下标i
    ( a ) F i n i s h [ i ] = f a l s e (a) Finish[i]=false (a)Finish[i]=false
    ( b ) R e q u e s t i ≤ W o r k (b)Request_i\leq Work (b)RequestiWork
    如果没有这样的i存在,转4
  3. W o r k = W o r k + A l l o c a t i o n i Work=Work+Allocation_i Work=Work+Allocationi
    F i n i s h [ i ] = t r u e Finish[i]=true Finish[i]=true, 转第2步
  4. 如果有一些 i ( 0 ≤ i < n ) , F i n i s h [ i ] = f a l s e , i(0\leq i<n),Finish[i]=false, i(0i<n),Finish[i]=false,则系统处在死锁状态,而且,如果Finish[i]=false, 则进程 P i P_i Pi是死锁的。

该算法需要 m × n 2 m\times n^2 m×n2次操作来判断是否系统处于死锁状态

  • 检测算法的例子
    五个进程P0到P4,三个资源类型A(7个实例),B(2个实例),C(6个实例)
    时刻T0的状态
    在这里插入图片描述
    对所有i,序列<P0,P2,P3,P1,P4>将导致Finish[i]=true。
    假定此时P2又请求了一个额外的C实例
    在这里插入图片描述
    这时,系统处于死锁状态。因为虽然可以归还P0所有的资源,但是资源不够完成其他进程的请求,所以存在一个包含P1,P2,P3和P4的死锁。
检测算法的用法

何时及多长时间的调用取决于

  1. 死锁可能发生的频率
  2. 当死锁发生时,有多少进程受影响。如果死锁经常发生,那么就该经常调用检测算法。
  3. 由于只有当某个进程提出请求,且得不到满足时才会出现死锁。因此极端情况下,每次请求不能立即满足时,就调用死锁检测算法。这样不仅能确定哪些进程处于死锁,而且能确定哪个进程造成死锁。但是对于每个请求都调用检测算法,会引起很大的计算开销。所以可以使用一个不太高的频率调用检测算法,如每小时一次,或当CPU使用率低于40%时。如果检测算法被随意的调用,可能图中存在很多的环以至于无法判断是哪一个进程引起死锁的发生。

死锁的恢复

  • 当死锁检测算法检测死锁已存在,可以采用多种措施恢复,如人工恢复自动恢复。可以终止进程来打破一个或多个进程等待或者从一个或多个进程中抢占一个或多个资源。

  • 人工恢复
    通知操作员,人工处理

  • 自动恢复
    终止进程,抢占资源

  • 但不管哪种方法,系统都会收回分配给被终止进程的所有资源。

终止进程
  1. 中断所有的死锁进程,但代价很大。这些进程中,有些可能已经计算了很长时间,计算结果必须放弃,以后还要重新计算
  2. 一次只中断一个进程直到死锁环消失。这种方法开销也很大,因为每终止一次进程,都要调用检测算法计算是否还有死锁。对于给定的死锁进程,必须确定终止哪个或哪些进程,理论上应该选代价最小的进程,但是代价最小并不精确,许多因素都影响选择进程。
  • 影响选择进程的因素
    进程的优先级;进程需要计算多长时间,以及需要多久时间结束;进程使用的资源;进程完成还需要多少资源;多少个进程需要被终止;进程是交互的还是批处理的。
抢占资源

把抢占资源给其他进程使用,直到死锁循环被打破为止。
这种方法需要考虑三个问题:

  1. 选择一个牺牲品,抢占哪些资源和哪些进程来使代价最小化(代价包括所拥有的资源数和运行到现在的时间等)
  2. 回滚:如果从一个进程抢占资源,那么必须将进程回滚到安全状态,然后重新开始进程。
  3. 饥饿:即保证资源不会总是从同一个进程被抢占。如果系统是基于代价来牺牲进程的,那么同样进程的可能总是被选中,这个进程永远都不能完成。显然需要一个进程有限地被选中。最常用的办法是在代价因素中加入回滚次数。

  1. 引起死锁的主要原因:竞争互斥资源,进程推进不当
  2. 死锁是没有外力干预,就会一直运行不下去。而饥饿是长时间等待,有机会运行的。死循环while True,如果没有外力干预也运行不下去。死循环是自己等自己运行完,不满足死锁的条件。
  3. 系统不会死锁看情况,可能永远不会有死锁,也可能在等待图变化后发生死锁。
  4. 虚拟化技术:设备管理中很重要的技术,把物理设备虚拟化为无数个虚拟设备,从而模拟成共享设备。
  5. 死锁会造成资源浪费,与安全性无关。
  6. 线程间存在死锁。
  7. 只有并发操作才有可能出现死锁。多道程序设计竞争资源,会发生死锁。并发操作带来的好处大于缺点。
  8. 有的资源不能被剥夺。
  9. 安全序列中,进程在运行完才会释放资源
  10. 死锁避免肯定会减少系统吞吐量,影响性能。
  11. 死锁判断算法时,占据CPU,单核的不会有新的进程进来。若多核,有新的进程出现,则需重新判断。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值