5. 死锁

  在多道程序环境中,多个进程可以竞争有限数量的资源。当一个进程申请资源时,如果这时没有可用资源,那么这个进程进入等待状态。有时,如果所申请的资源被其他等待进程占有, 那么该等待进程有可能再也无法改变状态。这种情况称为死锁(deadlock) 。

5.1 系统模型

  系统拥有有限数量的资源,需要分配到多个竞争进程。即多进程对共享资源的竞争可能导致死锁。
  在正常模式下,进程只能按如下顺序使用资源:
    1. 申请: 进程请求资源。如果申请不能立即被允许,那么申请进程应等待,直到它能获得该资源为止。
    2. 使用: 进程对资源进行操作。
    3. 释放: 进程释放资源。

5.2 死锁特征

  当死锁时,进程永远不能完成,系统资源被阻塞使用,以致于阻止了其他作业的开始执行。

5.2.1 死锁的必要条件

  如果在一个系统中以下四个条件同时成立,那么就能引起死锁:
    - 互斥(mutual exclusion) :至少有一个资源必须处于非共享模式, 即一次只有一个进程可使用。如果另一进程申请该资源,那么申请进程应等到该资源释放为止。
    - 占有并等待(hold and wait) :一个进程应占有至少一个资源, 并等待另一个资源, 而该资源为其他进程所占有。
    - 非抢占(no preemption) : 资源不能被抢占,即资源只能被进程在完成任务后自愿释放。
    - 循环等待(circular wait) :有一组等待进程{ P 0 P_0 P0 P 1 P_1 P1, ……, P n P_n Pn} , P 0 P_0 P0等待的资源为 P 1 P_1 P1占有, P 0 P_0 P0等待的资源为 P 2 P_2 P2占有,……, P n P_n Pn等待的资源为 P 0 P_0 P0占有。

  我们强调所有四个条件必须同时成立才会出现死锁。

  循环等待条件意味着占有并等待条件,这样四个条件并不完全独立。

5.3 死锁预防

  发生死锁有4个必要条件。只要确保至少一个必要条件不成立,就能预防死锁发生。

5.3.1 互斥

  互斥条件必须成立。也就是说,对共享资源的互斥申请。相反,可共享资源不要求互斥访问,因此不会死锁。比如,只读文件是一个很好的共享资源的例子。

5.3.2 持有且等待

  为了确保持有并等待条件不会出现在系统中,应保证:当每个进程申请一个资源时,它不能占有其他资源。
  一种可以采用的协议是,每个进程在执行前申请并获得所有资源。这可以这样实现:要求进程申请资源的系统调用在所有其他系统调用之前进行。
  另外一种协议允许进程仅在没有资源时才可申请资源。一个进程可申请一些资源并使用它们。然而,在它申请更多其他资源之前,它应释放现已分配的所有资源。
  这两种协议有两个主要缺点
   第一,资源利用率可能比较低,因为许多资源可能已分配,但是很长时间没有被使用。
   第二,可能发生饥饿。一个进程如需要多个常用资源,可能必须永久等待,因为在它所需要的资源中至少有一个已分配给其他进程。

5.3.3 无抢占

  不能抢占已分配的资源。为了确保这一条件不成立,可以采用如下协议:如果一个进程持有资源并申请另一个不能立即分配的资源(也就是说,这个进程应等待),那么它现在分配的资源都可被抢占。
  换句话说,这些资源都被隐式释放了。被抢占资源添加到进程等待的资源列表上。只有当进程获得其原有资源和申请的新资源时,它才可以重新执行。
  换句话说,如果一个进程申请一些资源,那么首先检查它们是否可用。如果可用,那么就分配它们。如果不可用,那么检查这些资源是否已分配给等待额外资源的其他进程。如果是,那么从等待进程中抢占这些资源,并分配给申请进程。如果资源不可用且也不被其他等待进程持有,那么申请进程应等待。当一个进程处于等待时,如果其他进程申请其拥有资源,那么该进程的部分资源可以被抢占。只有当一个进程分配到申请的资源,并且恢复在等待时被抢占的资源时,它才能重新执行。这个协议通常用于状态可以保存和恢复的资源, 如CPU寄存器和内存。它一般不适用于其他资源,如互斥锁和信号量。

5.3.4 循环等待

  确保这个条件不成立的方法是:对所有资源类型进行完全排序,且要求每个进程按递增顺序来申请资源。

5.4 死锁恢复

  当检测算法确定已有死锁时,存在多种可选方案。一种可能是,通知操作员死锁已发生, 并且让操作员人工处理死锁。另一种可能是, 让系统从死锁状态中自动恢复(recover)过来。
  打破死锁有两个选择。一个是,简单地中止一个或多个进程来打破循环等待。另一个是,从一个或多个死锁进程那里抢占一个或多个资源。