并发:死锁和饥饿

死锁原理:

    死锁定义为一组相互竞争系统资源或进行通信的进程间的“永久”阻塞。当一组进程中的每个进程都在等待某个时间,而只有这组进程中的其他被阻塞的进程才可以触发该时间,这时这组进程发生死锁。

 

可重用资源:

    资源通常可分为两类:可重用资源和可消耗资源。可重用资源是指一次只能供一个进程安全的使用,并且不会由于使用而耗尽的资源。如处理器,IO通道,内存和外存设备,以及诸如文件,数据库,信号量。


可消耗资源:

    可消耗资源是指可以被创建和销毁的资源。通常对某种类型可消耗资源的数目没有限制,一个无阻塞的生产进程可以创建任意数目的这类资源。当消费进程得到一个资源时,该资源就不再存在了。可消耗资源的例子有中断、信号、消息和IO缓冲区中的消息。


死锁的四个条件:

死锁有三个必要条件:

  1.互斥。一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。

  2.占有且等待。当一个进程等待其他进程时,继续占有已经分配的资源。

  3.不可抢占。不能强行抢占进程已占有的资源。


为了保证结果的一致性和数据库的完整性,互斥是非常必要的。同理,不能随意地进行资源抢占。比如,当涉及数据资源时,必须提供回滚恢复机制以支持资源抢占,这样才能把进程和它资源恢复到以前适当的状态,使得进程最终可以重复它的动作。

   前面三个条件都是死锁存在的必要条件,但不是充分条件。对死锁的产生还需要第四个条件:

4、循环等待。存在一个封闭的进程链,使得每个进程至少占有此链中下个进程所需的一个资源。


死锁预防:

1. 破坏“互斥”条件
破坏“互斥”条件,就是在系统里取消互斥。若资源不被一个进程独占使用,那么死锁是肯定不会发生的。
但一般来说在所列的四个条件中,“互斥”条件是无法破坏的。因此,在死锁预防里主要是破坏其他几个必要条件,而不去涉及破坏“互斥”条件。
2. 破坏“占有并等待”条件
破坏“占有并等待”条件,就是在系统中不允许进程在已获得某种资源的情况下,申请其他资源。即要想出一个办法,阻止进程在持有资源的同时申请其他资源。
方法一:创建进程时,要求它申请所需的全部资源,系统或满足其所有要求,或么什么也不给它。这是所谓的 “ 一次性分配”方案。
方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源S时,须先把它先前占有的资源R释放掉,然后才能提出对S的申请,即使它可能很快又要用到资源R。
3. 破坏“不可抢占”条件
破坏“不可抢占”条件,就是允许对资源实行抢夺。
方法一:如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次请求这些资源和另外的资源。
方法二:如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。只有在任意两个进程的优先级都不相同的条件下,方法二才能预防死锁。
4. 破坏“循环等待”条件
破坏“循环等待”条件的一种方法,是将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。这样做就能保证系统不出现死锁。

死锁的避免:


死锁的预防是通过破坏产生条件来阻止死锁的产生,但这种方法破坏了系统的并行性和并发性。
死锁产生的前三个条件是死锁产生的必要条件,也就是说要产生死锁必须具备的条件,而不是存在这3个条件就一定产生死锁,那么只要在逻辑上回避了第四个条件就可以避免死锁。
避免死锁采用的是允许前三个条件存在,但通过合理的资源分配算法来确保永远不会形成环形等待的封闭进程链,从而避免死锁。该方法支持多个进程的并行执行,为了避免死锁,系统动态的确定是否分配一个资源给请求的进程。方法如下:
1.如果一个进程的当前请求的资源会导致死锁,系统拒绝启动该进程;
2.如果一个资源的分配会导致下一步的死锁,系统就拒绝本次的分配;
显然要避免死锁,必须事先知道系统拥有的资源数量及其属性。


3.死锁检测

首先可以通过画分配图来判断是否发生了死锁。但如何用算法来判断呢?

死锁检测算法。算法使用的数据结构是如下这些:      

       占有矩阵A:n*m阶,其中n表示并发进程的个数,m表示系统的各类资源的个数,这个矩阵记录了每一个进程当前占有各个资源类中资源的个数。
       申请矩阵R:n*m阶,其中n表示并发进程的个数,m表示系统的各类资源的个数,这个矩阵记录了每一个进程当前要完成工作需要申请的各个资源类中资源的个数。
       空闲向量T:记录当前m个资源类中空闲资源的个数。
       完成向量F:布尔型向量值为真(true)或假(false),记录当前n个并发进程能否进行完。为真即能进行完,为假则不能进行完。
       临时向量W:开始时W:=T。

算法步骤:
     (1)W:=T,
     对于所有的i=1,2,...,n,
     如果A[i]=0,则F[i]:=true;否则,F[i]:=false
     (2)找满足下面条件的下标i:
     F[i]:=false并且R[i]〈=W
     如果不存在满足上面的条件i,则转到步骤(4)。
     (3)W:=W+A[i]
     F[i]:=true
     转到步骤(2)
     (4)如果存在i,F[i]:=false,则系统处于死锁状态,且Pi进程参与了死锁。什麽时候进行死锁的检测取决于死锁发生的频率。如果死锁发生的频率高,那麽死锁检测的频率也要相应提高,这样一方面可以提高系统资源的利用率,一方面可以避免更多的进程卷入死锁。如果进程申请资源不能满足就立刻进行检测,那麽每当死锁形成时即能被发现,这和死锁避免的算法相近,只是系统的开销较大。为了减小死锁检测带来的系统开销,一般采取每隔一段时间进行一次死锁检测,或者在CPU的利用率降低到某一数值时,进行死锁的检测。 

4.死锁的恢复  

  一旦在死锁检测时发现了死锁,就要消除死锁,使系统从死锁状态中恢复过来。  

    (1)最简单,最常用的方法就是进行系统的重新启动,不过这种方法代价很大,它意味着在这之前所有的进程已经完成的计算工作都将付之东流,包括参与死锁的那些进程,以及未参与死锁的进程。

    (2)撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素。 

  此外,还有进程回退策略,即让参与死锁的进程回退到没有发生死锁前某一点处,并由此点处继续执行,以求再次执行时不再发生死锁。虽然这是个较理想的办法,但是操作起来系统开销极大,要有堆栈这样的机构记录进程的每一步变化,以便今后的回退,有时这是无法做到的。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值