死锁
一、死锁的概念
在多道程序系统中,由于多个进程的并发执行,改善了系统资源的利用率并提高了系统的处理能力。然而,多个进程的并发执行也带来了新的问题——死锁。所谓死锁是指多个进程因竞争资源而造成的一种僵局,若无外力作用,这些进程都将无法向前推进。
1)系统资源的竞争
通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,似的进程在运行过程中会因争夺资源而陷入僵局。只有对不可剥夺资源的竞争才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
2)进程推进顺序非法
进程在运行过程中,请求和释放资源的顺序不当,同样会导致死锁。信号量使用不当也会造成死锁。进程间相互等待对方发来的消息,结果也会造成某些进程间无法继续向前推进。
3)死锁产生的必要条件
产生死锁必须同时满足以下四个条件,只要其中任一个条件不成立,死锁就不会发生。
互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占用。此时若有其他进程请求该资源,则请求进程只能等待。
不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放。
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。
二、死锁的处理策略
为使系统不发生死锁,必须设法破坏产生死锁的四个必要条件之一,或者允许死锁产生,但当死锁发生时能检测出死锁,并有能力实现恢复。
死锁处理策略有:
破坏死锁的四个必要条件之一。
用某种方式防止系统进入不安全状态。
允许进程在运行过程中发生死锁,通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。
防止死锁发生只需要破坏死锁产生的四个必要条件之一即可。
允许系统资源都能共享使用。(不太可行):当一个以保持了某些不可剥夺资源的进程,请求新的资源时得不到满足,它必须释放已经保持的所有资源,待以后需要时再重新申请。这种方法常用于状态易于保存和恢复的资源,如CPU的寄存器及内存资源,一般不能用于打印机之类的资源。
采用预先静态分配方法, 即进程在运行前一次申请完他所需要的全部资源,在他的资源未满足前,不把它投入运行。一旦运行后,这些资源就一直归它所有,也不再提出其他资源请求,这样就可以保证系统不会发生死锁。这种方式实现简单,但缺点也显而易见,系统资源被严重浪费,其中有些资源可能仅在运行初期或末期才使用,甚至根本不使用。而且还会导致“饥饿”现象,当由于个别资源长期被个别资源占用时,将只是等待该资源的进程迟迟不能开始运行。
破坏循环等待条件:为了破坏循环等待条件,可采用顺序资源分配法。首先给系统中的资源编号,规定每个进程,必须按编号递增的顺序请求资源,同类资源一次申请完。也就是说,只要进程提出申请分配资源,则该进程在以后的资源申请中,只能申请编号比之前大的资源。
四、死锁避免
避免死锁同样是属于事先预防的策略,但并不是事先采取某种限制措施破坏死锁的必须条件,而是在资源动态分配过程中,防止系统进入不安全状态,以避免死锁发生。这种方法所施加的限制条件较弱,可以获得较好的系统性能。
避免死锁的方法中,允许进程动态的申请资源,但系统在进行资源分配前,应先计算此次资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程,否则,让进程等待。
所谓安全状态,是指系统能按某种进程推进顺序,为每个进程分配其所需的资源,直至满足每个进程对资源的最大需求,是每个进程都可以顺序的完成。此时成P1P2P3。。为安全序列,如果系统无法找到一个安全序列,则称系统处于不安全状态。并非所有的不安全状态都是死锁状态,但当系统进入不安全状态后,便可能进入死锁状态;反之,只要系统处于安全状态,系统便可以避免进入死锁状态。
银行家算法是最著名的死锁避免算法。它提出的思想是:把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。操作系统按照银行家制定的规则为进程分配资源,当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足他的最大需求量,则按当前的申请量分配资源,否则就推迟分配。当进程在执行中继续申请资源时,先测试该进程已占用的资源数与本次申请的资源数之和是否超过了该进程对资源的最大需求量。若超过,则拒绝分配资源,若没有超过则在测试系统现存的资源能否满足该进程尚需的最大资源量,若能满足则按当前的申请量分配资源,否则也要推迟分配。
1)数据结构描述:
可利用资源适量Available:含有m个元素的数组,其中的每一个元素代表一个可用的资源数目,Aviailable【j】=K,则表示系统中现有Rj类资源K个。
最大需求矩阵Max:为n*m矩阵,定义了系统中n个进程中的每个进程中对m类资源的最大需求。Max【ij】=K,则表示进程i需要Rj类资源的最大数目为K。
分配矩阵Allocation:为n*m矩阵,定义了系统中每一类资源当前已分配给每一进程的资源数。Allocation【ij】=K,则表示进程i当前已分得Rj类资源的数目为K。
需求矩阵Need:为n*m矩阵,表示每个进程尚需的各类资源数。Need【ij】=K,则表示进程i还需要Rj类资源数目为K。
上述三个矩阵间存在下述关系:Need【ij】=Max【ij】-Allocation【ij】
2)银行家算法描述:
设Requesti表示进程Pi的请求适量,如果Requesti【j】=K,表示进程Pi需要Rj类资源K个。当Pi发出资源请求后,系统按下述步骤进行检查:
1如果Requesti[j]<=Need[I,j],便转向步骤2;否则,认为出错,因为它所需要的资源已超过它所宣布的最大值。
2如果Requesti【j】<=Available【j】,便转向步骤3;否则,表示尚无足够资源,Pi需等待。
3系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值:
Available【j】=Available【j】-Requesti【j】;
Allocation【ij】=Allocation【ij】+Requesti【j】;
Need【ij】=Need【ij】-Requesti【j】;
4系统执行安全性算法,检查此次西苑分配后,系统是否出于安全状态,若安全,才正式把资源分配给进程Pi,已完成本次分配;否则,将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。
3)安全性算法
1设置两个矢量。工作矢量work:它表示系统可供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,work=Available;Finish:它表示系统是否有足够的资源分配给进程,使之运行完成。开始时,Finish【i】=false;当有足够资源分配给进程Pi时,再令Finish【i】=true。
2从进程集合中找到一个能满足下述条件的进程:Finish【i】=false;Need【ij】<=work【j】;若找到,执行下一步骤,否则,执行步骤4。
3当进程Pi获得资源后,可顺利执行,直至完成,并释放粗分配给它的资源,故应执行:
Work【j】=work【j】+Allocation【ij】;
Finish【i】=true;Go to step2;
4如果所有进程的Finish【i】=true都满足,则表示系统处于安全状态;否则,系统处于不安全状态。
五、死锁的检测和解除
前面介绍的死锁预防和死锁避免都是在为进程分配资源时施加限制条件或进行检测,若系统为进程分配资源时不采取任何措施,则应该提供死锁检查和解除的手段。
系统死锁,可利用资源分配图来描述。用圆圈代表一个进程,用方框代表一类资源。由于一种类型的资源可能有很多歌,用框中的一个点代表一类资源中的一个资源。从进程到资源的有向边叫请求边,表示该进程请申请一个单位的该类资源;从资源到进程的边叫做分配边,
表示该类资源已经有一个资源被分配到了该进程。可以通过将资源分配图简化的方法来检测系统状态S是否为死锁状态。简化方法如下:
1)在资源分配图中,找出既不阻塞又不是孤点的进程Pi(即找出一条有向边与它相连,且该有向边对应资源的申请数量小于系统中已有空闲资源数量。若所有的连接该进程的边都满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的所有资源)。消去它所有的请求边和分配边,使之成为孤立的节点。
2)进程Pi所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。S为死锁的条件是当且仅当S状态的资源分配图是不可简化的,该条件为死锁定理。一旦检测出死锁,则应立即采取相应的措施,已解除死锁。死锁解除的主要方法有:
①资源剥夺法。挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源时,而处于资源匮乏的状态。
②进程撤销法。强制撤销一个或一部分进程并剥夺这些进程的资源。撤销的原则可以按进程的优先级和撤销进程代价的高低进行。
③进程回退法。让一个或多个进程回退到足以回避死锁的地步,进程回退时资源释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。