Linux下的死锁

来源:互联网 发布:阿里云部署网站 编辑:程序博客网 时间:2024/06/07 06:37

本文参考《操作系统精髓设计原理》第六章(178~191)
一. 死锁的概念
  我们可以把死锁定义为一组相互晶振系统资源或通信的进程间的“永久”阻塞。死锁都会涉及两个或者两个以上进程间对资源的冲突。
  一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占有的,该线程又被挂起没有机会释放锁,因此就永远处于挂起等待状态了,这叫做死锁(Deadlock)。 另一种典型的死锁情形是这样:线程A获得了锁1,线程B获得了锁2,这时线程A调lock试图获得锁2,结果是需要挂起等待线程B释放锁2,但这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了(如下图所示)。如果涉及到更多的线程,死锁的概率便会大大提升。
死锁
二. 死锁的条件
  死锁有三个必要条件:
1)互斥。一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。
2)占有且等待。当一个进程等待其他进程时,继续占有已分配的资源。
3)不可抢占。不能强行抢占进程已占有的资源。
  这三个是死锁的必要条件,但并不是充分条件,对于死锁仍然需要第四个条件:
4)循环等待。存在一个封闭的进程链,使得每个进程至少占有此链中下一个进程所需要的下一资源。如上图所示。
  这四个条件构成了死锁的充分必要条件,之所以循环等待可以造成死锁,是因为前面三个的约束条件。使得循环等待不可解。
四.死锁的解决方案
  由以上分析可以得出,死锁必须满足一定的条件,有三种方案可以解决,第一种是消除1到4的某一条件(预防),第二种资源分配做动态选择(避免 ),第三种是检测死锁的存在并试图恢复(检测)。
1)预防死锁
  预防死锁可以从两个角度进行,一,去除前三个必要条件(间接预防);二,防止循环等待(直接预防)。
1.直接预防
  对于互斥来说,这个条件是不可能禁止的,因为操作系统就是互斥的。对于占有且等待,如果要预防,需要要求进程一次申请所有所需要的资源。没申请到就一直等,它有很大缺点,有时我们并不知道要用多大,就算知道,申请后,也只会有一部分被使用。对于不可抢占,可以采取,当我们去申请一块资源时,请求被拒绝时,我必须释放我拥有的资源,当然我也可以继续申请刚被我释放的资源,或者我申请一块被占用的资源时(我优先级高),操作系统可以要求它释放资源给我。这些前提是资源可以被保存和恢复的情况下,才可以使用。 这些方法
2.防止循环等待
  循环等待条件可以通过定义资源类型的线性顺序来预防。如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁。比如一个个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定先后顺序比较困难,则应该尽量使pthread_mutex_trylock调用代替pthread_mutex_lock,以免死锁。
2)死锁避免
  预防死锁解决方案会导致低效率的资源使用和进程执行,死锁避免允许三个必要条件通过,但是否允许当前资源分配请求通过是否可以导致死锁决定。方法如下(具体算法见书):
1.如果一个进程的请求会导致死锁,则不启动此进程。
2.如果一个进程的增加的资源强求会导致死锁,则不允许此分配。
3)死锁检查
  死锁检测策略则完全相反,它不限制资源访问或约束进程行为,只要有可能被请求的资源就被授权给进程,操作系统周期性的执行条件4(循环等待)。这种算法可以尽早检测死锁情况,但会浪费大量时间(具体算法参照书)。
  检测到死锁后,需要以某种策略进行恢复。常用的有:取消所有死锁进程;把每个进程回滚到前面定义的某些检测点,重启进程;取消所有死锁进程;抢占死锁进程的资源。对于后面两种比较暴力,可以从占用资源量,以及优先级角度进行考虑。
  综上,每种方案都有优缺点,要根据具体的情况使用不同的方案。

原创粉丝点击