死锁原理:
死锁定义为一组相互竞争系统资源或进行通信的进程间的“永久”阻塞。当一组进程中的每个进程都在等待某个时间,而只有这组进程中的其他被阻塞的进程才可以触发该时间,这时这组进程发生死锁。
可重用资源:
资源通常可分为两类:可重用资源和可消耗资源。可重用资源是指一次只能供一个进程安全的使用,并且不会由于使用而耗尽的资源。如处理器,IO通道,内存和外存设备,以及诸如文件,数据库,信号量。
可消耗资源:
可消耗资源是指可以被创建和销毁的资源。通常对某种类型可消耗资源的数目没有限制,一个无阻塞的生产进程可以创建任意数目的这类资源。当消费进程得到一个资源时,该资源就不再存在了。可消耗资源的例子有中断、信号、消息和IO缓冲区中的消息。
死锁的四个条件:
死锁有三个必要条件:
1.互斥。一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。
2.占有且等待。当一个进程等待其他进程时,继续占有已经分配的资源。
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)撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素。
此外,还有进程回退策略,即让参与死锁的进程回退到没有发生死锁前某一点处,并由此点处继续执行,以求再次执行时不再发生死锁。虽然这是个较理想的办法,但是操作起来系统开销极大,要有堆栈这样的机构记录进程的每一步变化,以便今后的回退,有时这是无法做到的。