操作系统学习笔记(6)——进程死锁



1、进程死锁的概念与条件

  • 死锁定义
    • 背景:多道进程的并发执行改善系统的资源利用率,但也可能进程相互等待对方释放资源才能继续运行。
    • 死锁:指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
  • 死锁场景
    • 申请同类资源
      • 内存资源有m个分配单位
      • n个进程共享内存资源
      • 进程每次只能申请一个单位
      • 满足总理才能使用
      • 每个进程使用玩一次性释放
    • 申请不同类资源
  • 死锁条件
    • 互斥使用(资源独占):指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放
    • 不可强占(不可剥夺):指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放
    • 请求保持(部分分配,占有申请)︰指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放
    • 环路等待:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源

    



 

2、预防机制

  • 原理
    •  预先确定资源分配,保证不发生死锁
    • 通过破坏死锁4个必要条件之一来实现
    • 破坏“互斥使用”这一必要条件不现实
  • 解决方案
    • 破坏“不可剥夺”
      • 允许进程动态申请资源
      • 进程在申请新资源不能得到满足而变为等待状态之前,必须释放已占有的资源
      • 若需要资源必须重新申请
    • 破坏“请求保持”
      • 不允许进程动态申请资源
      • 进程运行前须一次性申请所需的所有资源
      • 进程所要资源均可满足时给予一次性分配
    • 破坏“循环等待”
      • 采用资源有序分配法
      • 系统中所有资源编号
      • 进程须严格按资源编号的递增次序申请资源
      • 违反上述规则操作系统不予分配

      



3、避免机制

  • 原理
    • 对进程发出的每一个资源申请进行动态检查
    • 根据检查结果决定是否分配资源
    • 若试分配后可能发生死锁,则不予分配,否则分配
  • 银行家算法
    • 概览:Dijkstra在1965年提出的银行家算法是著名的死锁避免算法,这个用于一个银行家给多个顾客贷款的算法可以直接用于操作系统给进程分配资源,这时只要把银行家换成操作系统,把顾客换成进程,把资金换成资源,把银行家决定是否放贷时所用的判断过程(即判断顾客是否有信誉和偿还能力)换成操作系统决定是否分配资源时所用的判断过程(即判断进程是否能及时归还资源)即可。
    • 问题描述:银行家拥有一笔周转资金。客户要求分期贷款,如果能够得到各期贷款,就—定能够归还贷款,否则就一定不能归还贷款。银行家应谨慎地贷款,防止出现坏账。银行家采用的具体方法是看是否有足够的剩余资金满足某一客户,如此反复下去。如果所有投资最终都被收回,则请求可以批准。
    • 问题转译:若在某一时刻,系统能按某种进程顺序,如{P1,P2,…,Pn},为每个进程分配其所需的资源,直至最大需求,使每个进程均可顺利完成,则称此时系统的状态为安全状态,称这样的一个进程序列{P1,P2,…,Pn},为安全序列。安全序列的实质是:序列中的每一个进程Pi( i= 1,2,… ,n)到运行完成尚需的资源量不超过系统当前剩余的资源量与所有在序列中排在它前面的进程当前所占有的资源量之和。若在某一时刻,系统中不存在一个安全序列,则称系统处于不安全状态
    • 需注意:(1)系统在某一时刻的安全状态可能不唯一,但这不影响对系统安全性的判断。(2)安全状态是非死锁状态,而不安全状态并不一定是死锁状态。即系统处于安全状态一定可以避免死锁,而系统处于不安全状态则仅仅可能进入死锁状态。

 

    • 银行家算法的实质就是要设法保证系统动态分配资源后不进入不安全状态,以避免可能产生的死锁。即没当进程提出资源请求且系统的资源能够满足该请求时,系统将判断满足此次资源请求后系统状态是否安全,如果判断结果为安全,则给该进程分配资源,否则不分配资源,申请资源的进程将阻塞。
    • 相关数据结构:
      • 可利用资源向量Available。这是一个含有m个元素的数组,其中的而每一个元素代表一类可利用资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态的改变。如果Available[j]=K,则表示系统中现有Rj类资源K个。
      •  最大需求矩阵Max。这是一个n*m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=K;则表示进程i需要Rj类资源的最大数目为K。
      • 分配矩阵Allocation。这也是一个n*m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。
      • 需求矩阵Need。这也是一个n*m的矩阵,用以表示每一个进程尚需的各类资源数。如果Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成任务。
      • 三者关系:Need[i,j]=Max[i,j]-Allocation[i,j] 
    • 设计思路:
      • 第一部分:银行家算法模块:
        • 1.如果Request<=Need,则转向2;否则,出错。
        • 2.如果Request<=Available,则转向3,否则等待 。
        • 3.系统试探分配请求的资源给进程。
        • 4.系统执行安全性算法。
      • 第二部分:安全性算法模块:
        • (1)设置两个向量  
          • ①工作向量:Work=Available(表示系统可提供给进程继续运行所需要的各类资源数目)  
          • ②Finish:表示系统是否有足够资源分配给进程(True:有;False:没有).初始化为False 
        • (2)若Finish[i]=False&&Need<=Work,则执行3;否则执行4(i为资源类别) 
        • (3). 进程P获得第i类资源,则顺利执行直至完成,并释放资源: Work=Work+Allocation; Finish[i]=true;转2 
        • (4)若所有进程的Finish[i]=true,则表示系统安全;否则,不安全!
    • 详细设计:
      • 在系统运行过程中,对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统可能发生死锁,则不予分配,否则予以分配 。因此,对资源的分配要给予合理的规划。
      • 设Request i是进程Pi的申请向量,如果Request i[j]=K,则表示进程Pi需要K个Rj类型的资源。当Pi发出资源请求后,系统按下述步骤进行检查:
        1. 如果Request i[j]<=Need[i,j],便转向步骤2);否则认为出错,因为它所需要的资源数已经超过它所宣布的最大值。
        2. 如果Request i[j]<=Available[i,j],便转向步骤3);否则,表示尚无足够资源,Pi需等待。
        3. 系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值:
          • Available[j]:=Available[j]-Request i[j];
          • Allocation[i,j]:=Allocation[i,j]+Request i[j];
          • Need[i,j]:=Need[i,j]-Request i[j
        4. 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配;否则,将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。
    • 系统所执行的安全性算法可描述
      1. 设置两个向量
        • 工作向量Work,它表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,Work:=Available。
        • Finish,它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做Finish[i]:=false;当有足够资源分配给进程时,再令Finish[i]:=ture.
      2. 从进程集合中找到一个满足下述条件的进程:
        • Finish[i]=false;
        • Need[i,j]<=Work[j];若找不到,执行步骤3),否则,执行步骤4)。
      3. 当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
        • Work[j]:=Work[j]+Allocation[i,j];
        •  Finish[i]:=true;
        • Go to step 2;
      4. 如果所有进程的Finish[i]=true都满足,则表示系统处于安全状态;否则,系统处于不安全状态。

 

    • eg1.
      • 假定系统中有五个进程{p0,p1,p2,p3,p4}和三类资源{A,B,C}各类资源的数目分别是10,5,7,已知T0时刻资源分配情况如下: 

  

 

      • 如上图所示是我们的已知条件也就是我们开始时候的资源分配状态,接下来我们要求这个状态下的安全序列,也就是将可利用的资源分配给某个需要资源小于可利用资源的进程,让这个进程运行完成,进程运行完成之后已分配的资源就会被释放,然后继续执行上述操作,只要能找到满足Need小于Available,就分配资源,让该进程运行完成,最后将资源释放,也就是将已分配的资源加到可利用的资源,如果最后每一个进程都能运行完成就得到了我们的安全序列,所以系统就是安全的,如果存在一个进程,可利用资源不能满足需求就不再分配
      • 过程:
        • 将Available和Need对比,P0的Need>Available,不满足,往后找, 
        • P1的Available小于Need,分配给P1,P1就能运行完成,最后释放资源{2,0,0} ,所以现在的资源就是{3,3,2}+{2,0,0}={5,3,2} 
        • 然后继续向下找,p2不满足条件,P3满足条件,将资源分配给P3,让其运行完成,并释放空间{2,1,1},所以现在的资源就是{5,3,2}+{2,1,1}={7,4,3}
        • 依次类推得到安全序列为{P1,P3,P4,P2,P0} 
      • 过程如下图,其中finish表示进程运行完成 

 

    • eg2.
      • 假设P1请求资源Request{1,0,2},要怎样处理呢 ?首先要检测看请求资源是不是比可利用资源和需要资源小,如果其中一个或两个条件都不满足,进程等待,如果满足进行下面操作, 这里{1,0,2}是满足这两个条件的 。我们进行的操作就是将请求资源加到Allocation上面,也就是已分配的资源得到扩充,同样的请求的资源一定是来自可利用资源的,所以可利用资源要减上一个请求资源数目,因为Need和Allocation的和是一个固定值max所以相应的Allocation加上一个数值,Need就要减上一个数值,变化之后如下图所示: 

 

      • 然后再像前面一样,把这个表当成T0时刻的资源分配状态,再来找安全序列,判断系统是不是安全的。


4、检测和解决

  • 原理
    • 允许死锁发生
    • 系统不断监视进展情况,判断死锁是否发生

    • 一旦死锁发生则采取专门的措施,解除死锁并以最小的代价恢复运行

    • 检测时机:定时检测、进程等待、资源利用率下降
    • 检测手段:进程-资源分配图
  • 死锁检测
    • 检测模型

      • 方框表示资源类
      • 黑圆点表示资源实例
      • 圆圈中加进程名表示进程
      • 资源实例指向进程的一条有向边来表示分配边
      • 进程指向资源类的一条有向边来表示申请边
      • 检测“进程-资源分配图”是否可完全简化
    • 检测步骤
      • ①找一个只有分配边的非孤立进程结点,去掉分配边将其变为孤立结点;若找不到则转③
      • ②将资源分配给一个等待资源的进程,将某进程的申请边变为分配边,转①
      • ③图中有进程不是孤立结点,则此图不可完全简化,满足死锁的充分条件,系统为死锁状态

    • 死锁解除
      • 资源剥夺法
        • 从其他进程那里剥夺足够数量的资源给死锁进程,以解除死锁状态
      • 撤销进程法
        • 撤消全部死锁进程,恢复到正常状态,简单但代价太大
        • 按照某种顺序逐个撤消死锁进程,直到有足够的资源供其他未被撤消的进程使用,以消除死锁状态

          



 

5、对死锁的思考

  • 原因
    • 系统资源不足
    • 进程运行推进的顺序不合适
    • 资源分配不当
  • 解决原则
    • 单独使用死锁预防、避免、检测与解除并不能全面解决操作系统中遇到的所有死锁问题
    • 可将系统中的进程、资源分为若干类,对每一类进程、资源使用最适合它的办法解决死锁


 

 

 

posted @ 2021-10-08 13:56  从未想过的想  阅读(591)  评论(0编辑  收藏  举报