死锁

566 阅读8分钟

什么是死锁?

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。如下图所示:

image.png

死锁的四个必要条件

上面的情况只是死锁的一个例子,我们可以用更精确的方式描述死锁出现的条件:

  • 互斥。资源被竞争性地访问,这里的资源可以理解为锁;
  • 持有并等待。线程持有已经分配给他们的资源,同时等待其他的资源;
  • 不抢占。线程已经获取到的资源不会被其他线程强制抢占;
  • 环路等待。线程之间存在资源的环形依赖链,每个线程都依赖于链条中的下一个线程释放必要的资源,而链条的末尾又依赖了链条头部的线程,进入了一个循环等待的状态。

上面这四个都是死锁出现的必要条件,如果其中任何一个条件不满足都不会出现死锁。虽然这四个条件的定义看起来非常的理论和官方,但是在实际的编程实践中,我们正是在死锁的这四个必要条件基础上构建出解决方案的。所以这里不妨思考一下这四个条件各自的含义,想一想如果去掉其中的一个条件死锁是否还能发生,或者为什么不能发生。

死锁的处理办法

  • 死锁预防
  • 死锁避免
  • 死锁检测
  • 死锁恢复

1. 死锁预防

那么想要预防死锁的发生,我们自然是要让死锁无法成立,最直接的方法当然是破坏掉死锁出现的必要条件。只要有任何一个必要条件无法成立,那么死锁也就没办法发生了。

 一般来说互斥条件是无法破坏的,所以在预防死锁时主要从其他三个方面入手 :

(1)破坏持有并等待:

在系统中不允许进程在已获得某种资源的情况下,申请其他资源,即要想出一个办法,阻止进程在持有资源的同时申请其它资源。

方法一:在所有进程开始运行之前,必须一次性的申请其在整个运行过程中所需的全部资源,这样,该进程在整个运行期间便不会再提出资源请求,从而破坏了“请求”条件。系统在分配资源时,只要有一种资源不能满足进程的需要,即使其它所需的各资源都空闲也不分配给该进程,而让该进程等待,由于该进程在等待期间未占用任何资源,于是破坏了“保持”条件。

方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源S时,需要先把它先前占有的资源R释放掉,然后才能提出对S的申请,即使它很快又要用到资源R。

两种协议比较:第二种协议优于第一种协议,因为第一种协议会造成资源的严重浪费,使资源利用率大大的降低,也会由于占据大量资源导致其它进程的饥饿问题。

(2)破坏不可抢占条件:允许对资源实行抢夺。

方式一:如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次请求这些资源和另外的资源。

方式二:如果一个进程请求当前被另一个进程占有的资源,则操作系统可以抢占另一个进程,要求它释放资源,只有在任意两个进程的优先级都不相同的条件下,该方法才能预防死锁。

(3)破坏环路等待条件

对系统所有资源进行线性排序并赋予不同的序号,这样我们便可以规定进程在申请资源时必须按照序号递增的顺序进行资源的申请,当以后要申请时需检查要申请的资源的编号大于当前编号时,才能进行申请。

2. 死锁避免

系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。

死锁预防和死锁避免的区别

预防是通过破坏产生死锁的四个必要条件来避免死锁的发生。 避免是在资源动态分配的过程中防止系统进入不安全的状态来避免死锁的发生。

3. 死锁检测

1、画出资源分配图

系统死锁,可利用资源分配图来描述。如下图所示,用长方形代表一个进程,用框代表一类资源。由于一种类型的资源可能有多个,用框中的一个点代表一类资源中的一个资源。从进程到资源的有向边叫请求边,表示该进程申请一个单位的该类资源;从资源到进程的边叫分配边,表示该类资源已经有一个资源被分配给了该进程。

image.png

2、简化资源分配图

第一步:先看A资源,它有三个箭头是向外的,因此它一共给进程分配了3个资源,此时,A没有空闲的资源剩余。

第二步:再看B资源,它有一个箭头是向外的,因此它一共给进程分配了1个资源,此时,B还剩余一个空闲的资源没分配。

第三步:看完资源,再来看进程,先看进程P2,它只申请一个A资源,但此时A资源已经用光了,所以,进程P2进入阻塞状态,因此,进程P2暂时不能化成孤立的点。

第四步:再看进程P1,它只申请一个B资源,此时,系统还剩余一个B资源没分配,因此,可以满足P1的申请。这样,进程P1便得到了它的全部所需资源,所以它不会进入阻塞状态,可以一直运行,等它运行完后,我们再把它的所有的资源释放。相当于:可以把P1的所有的边去掉,变成一个孤立的点,如下图所示:

image.png

第五步:进程P1运行完后,释放其所占有的资源(2个A资源和1个B资源),系统回收这些资源后,空闲的资源便变成2个A资源和1个B资源,由于进程P2一直在申请一个A资源,所以此时,系统能满足它的申请。这样,进程P2便得到了它的全部所需资源,所以它不会进入阻塞状态,可以一直运行,等它运行完后,我们再把它的所有的资源释放。相当于:可以把P2的所有的边都去掉,化成一个孤立的点,变成下图:

image.png (若能消去图中所有的边,则称该图是可完全简化的,如上图)

3、使用死锁定理判断

死锁定理:

  • ①如果资源分配图中没有环路,则系统没有死锁;
  • ②如果资源分配图中出现了环路,则系统可能有死锁。 或者说: 当且仅当S状态的资源分配图是不可完全简化的时候,系统状态则是死锁状态

死锁恢复

1、资源剥夺法

挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源,而处于资源匮乏的状态。

2、撤销进程法

强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。

3、进程回退法

让一(多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。

参考:blog.csdn.net/hd12370/art…

blog.csdn.net/ypt523/arti…

blog.csdn.net/jgm20475/ar…