作为开发者,我们必须深入了解死锁的概念、产生原因、检测方法以及应对策略,以确保程序的稳定性和可靠性
本文将详细剖析Linux程序死锁,并提供一系列有效的解决方案
一、死锁的基本概念 死锁是指在多道程序系统中,一组进程中的每个进程都无限期地等待被该组进程中的另一个进程所占有且永远不会被释放的资源,这种现象称为系统处于死锁状态,简称死锁
处于死锁状态的进程成为死锁进程
系统发生死锁会大量浪费系统资源,甚至会导致整个系统崩溃
二、死锁的产生原因 死锁的产生原因主要有两个:一是竞争资源,系统提供的资源有限,不能满足每个进程的需求;二是多道程序运行时,进程的推进顺序不合理
系统的资源分为两类:永久性资源和临时性资源
永久性资源(可重生资源)是指那些可供进程重复利用、长期存在的资源,如内存、CPU等硬件资源,以及数据文件、共享程序代码等软件资源
临时性资源(消耗性资源)是指由某个进程产生、只为另一个进程使用一次,或经过短暂时间后便不可再使用的资源,如I/O和时钟中断、消息等
两种资源都可能导致发生死锁
三、死锁的必要条件 对于永久性资源,产生死锁有四个必要条件: 1.互斥条件:进程独占所分配到的资源且排他使用
进程互斥使用资源,即任意时刻一个资源只能被一个进程使用,其他进程申请一个正在被占有的资源时,申请者要等待直至资源被占用者释放
2.不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行剥夺,只能由使用者自愿释放
3.请求和保持条件:进程已经得到至少一个资源,但又提出了新的资源请求,而该资源又被其他进程所占有,此时进程会等待直至得到所需资源,在等待期间继续占用已得到的资源
4.循环等待条件:在发生死锁时,必然存在一个进程等待队列,其中每个进程所占有的资源同时被另一个进程所申请,即前一个进程占有后一个进程所申请的资源,形成一个进程等待环路
四、死锁的检测与解除 解决死锁的方法可分为两类:一是不让死锁发生;二是等死锁发生后再解决
具体有以下四种方法: 1.预防死锁:通过破坏产生死锁的必要条件(除第一个互斥条件外的其他条件)来防止死锁发生
此方法会导致系统资源利用率过低
2.避免死锁:在资源的动态分配过程中,采取某种方法防止系统进入不安全状态,从而避免死锁发生
此方法只需以较弱的限制条件为代价,并获得较高的资源利用率
3.检测死锁:允许系统运行过程中发生死锁,事先不用采取预防、避免措施
4.解除死锁:与死锁检测相配套的措施,用于将进程从死锁状态下解脱出来
在允许进程动态申请资源的前提下,可以做出如下规定:一个进程在申请新资源的要求不能立即得到满足时,该进程进入等待状态
而处于等待状态下的进程的全部资源可以被他人剥夺,被剥夺的资源重新放到资源表中
该方法适合那些状态是容易保存和恢复的资源,例如CPU、内存等
但此方法实现起来较为复杂,且代价很大
因为一个资源在使用一段时间后被强制剥夺会造成前阶段工作失效,甚至可能出现某个进程反复申请和释放资源的情况,使得进程执行无限期推迟,还增加了系统开销,延长了进程的周转时间,降低了系统的吞吐量和性能
五、死锁的预防策略 预防死锁的主要策略是破坏前面提到的四个必要条件之一: 1.破坏互斥条件:使资源尽可能变为共享资源
某些资源(如读写锁)可以允许多个线程同时访问
2.破坏请求和保持条件:要求进程在开始时一次性申请所有需要的资源
这样可以避免在获得部分资源后继续等待其他资源的情况
3.破坏不可剥夺条件:允许操作系统强制剥夺某些资源
在某些情况下,如果一个进程需要其他资源而无法获取,可以通过释放当前资源,等待一段时间后重新尝试获取所有资源
4.破坏循环等待条件:为所有资源排序,并要求进程按照预定义的顺序请求资源
这样可以避免循环等待的发生
六、死锁的避免策略 避免死锁的基本思想是:系统对进程发出的每个系统能满足的资源申请进行动态检测,并根据检查结果决定是否分配资源;如果分配后系统可能发生死锁,则不予分配,反之予以分配
由于避免死锁策略中允许进程动态地申请资源,所以系统要提供某种方法,在分配资源前,先分析资源分配的安全性
当估计到可能有死锁发生时及时设法避免
如果操作系统能保证所有进程能在有限时间内获得需要的全部资源,则称系统处于“安全状态”,否则就是不安全的
所谓的安全状态是指,如果系统的所有进程构成了一个安全序列,则系统处于安全状态
银行家算法是最经典的死锁避免算法之一
七、死锁的检测与解除策略 死锁检测的实质是确定是否存在“循环等待”条件,检测算法确定死锁发生并识别出与死锁有关的进程和资源
1.死锁检测算法:当任一进程申请一个已被其他进程占有的资源时,通过反复查找资源分配表和进程等待表,来确定该进程对这个资源的申请是否会导致环路,若是,便确定出现死锁
2.死锁解除:要解除死锁就要剥夺资源,那就要考虑如下几个问题:被牺牲的进程重新运行或回退到某一点继续运行;如何保证不发生“饿死现象”,即如何保证不会总是剥夺同一个进程的资源,从而导致该进程处于“饥饿状态”;“最小代价”,即最经济合算的算法,使得进程回退带来的开销最小
八、实例分析:死锁的代码示例
下面是一个简单的死锁代码示例,在该示例中,两个线程分别获取不同的锁,然后尝试获取对方已经占有的锁,最终导致死锁:
include