Deadlock

在许多应用中进程需要以独占的方式访问资源,当操作系统允许多个进程并发执行时可能会出现进程永远被阻塞现象,如两个进程分别等待对方所占的资源,于是两者都不能执行而处于永远等待状态,此现象称为死锁

死锁通常被定义为:如果一个进程集合中的每个进程都在等待只能由此集合中的其他进程才能引发的事件,而无限期陷入僵持的局面称为死锁。

产生条件

  • 互斥条件
    临界资源是独占资源,进程应互斥且排他的使用这些资源。

  • 占有和等待条件
    进程在请求资源得不到满足而等待时,不释放已占有资源。

  • 不剥夺条件
    又称不可抢占,已获资源只能由进程自愿释放,不允许被其他进程剥夺。

  • 循环等待条件
    又称环路条件,存在循环等待链,其中,每个进程都在等待链中等待下一个进程所持有的资源,造成这组进程处于永远等待状态。

死锁只有在这四个条件同时满足时出现

产生原因

  • 进程顺序不当
  • PV操作使用不妥
  • 同类资源分配不均
  • 对某些资源的使用未加限制

产生死锁的原因不仅与系统拥有的资源数量有关,而且与资源分配策略、进程对资源的使用要求以及进程的推进顺序有关。

解决方法

主要有一下三种方法:

  • 死锁防止
  • 死锁避免
  • 死锁检测和恢复

预防

在程序运行之前防止发生死锁。

前面说了死锁产生的条件有四个,分别是:互斥条件、占有和等待条件、不剥夺条件、循环等待条件。

而死锁预防的策略就是至少破坏这四个条件其中一项。

  1. 破坏互斥条件

    使资源同时访问而非互斥使用,就没有进程会阻塞在资源上,从而不发生死锁。

    只读数据文件、磁盘等软硬件资源均可采用这种办法管理;
    但是许多资源是独占性资源,如可写文件、键盘等只能互斥的占有;
    所以这种做法在许多场合是不适用的。

  2. 破坏占有和等待条件

    一种实现方式是规定所有进程在开始执行前请求所需要的全部资源

  3. 破坏不剥夺条件

  4. 破坏循环等待条件

    给资源统一编号,进程只能按编号顺序来请求资源。

避免

在程序运行时避免发生死锁。

各种死锁预防方法能够防止发生死锁,但必然会降低系统并发性,导致低效的资源利用率,会严重地损害系统性能。

因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法

安全状态

图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。

安全状态定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。

单个资源的银行家算法

一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。

上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。

多个资源的银行家算法

上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0

检查一个状态是否安全的算法如下:

  • 查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。
  • 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 A 中。
  • 重复以上两步,直到所有进程都标记为终止,则状态时安全的。

如果一个状态不是安全的,需要拒绝进入这个状态。

检测

对资源的分配加以适当限制可防止或避免死锁发生,但不利于进程对系统资源的充分共享。

不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。

如果进程 - 资源分配图中无环路,此时系统没有发生死锁
如果进程 - 资源分配图中有环路,则可分为以下两种情况:

  • 每种资源类中仅有一个资源,则系统发生了死锁。此时,环路是系统发生死锁的充分必要条件,环路中的进程就是死锁进程。
  • 每种资源类中有多个资源,则环路的存在只是产生死锁的必要不充分条件,系统未必会发生死锁。

下面详细说一下这两种情况:

每种资源类中仅有一个资源的死锁检测

上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。

图 a 可以抽取出环,如图 b,它满足了环路等待条件,因此会发生死锁。

每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。

比如:进程D需要的资源是U、T;进程G需要的资源是V、U;进程E需要的资源是T、V 。此时进程D占有资源U,进程E占有资源T,进程G占有资源V。所以此时导致进程D、E、G所申请的资源不能得到全部满足,陷入死锁。

死锁检测算法:

每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。

每种资源类中有多个资源的死锁检测

每个资源类用一个方框表示,方框中的原点表示此资源类中的各个资源;

每个进程用一个圆圈来表示,用有向边表示进程申请资源和资源分配情况。

约定方框→圆圈表示资源分配,圆圈→方框表示申请资源。

这种情况下,图 3-6 发生了死锁,而图 3-7 没有发生死锁。

死锁检测算法:

上图中,有三个进程四个资源,每个数据代表的含义如下:

  • E 向量:资源总量
  • A 向量:资源剩余量
  • C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量
  • R 矩阵:每个进程请求的资源数量

进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。

算法总结如下:

每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。

  1. 寻找一个没有标记的进程 Pi,它所请求的资源小于等于 A。
  2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回第1步。
  3. 如果没有这样一个进程,算法终止。

恢复

  • 资源剥夺法
    剥夺陷于死锁的进程所占用的资源,但并不撤销此进程,直至死锁解除。

  • 进程回退法
    根据系统保存的检查点让所有的进程回退,直到足以解除死锁,这种措施要求系统建立保存检查点、回退及重启机制。

  • 进程撤销法

    撤销陷入死锁的所有进程,解除死锁,继续运行。

    逐个撤销陷入死锁的进程,回收其资源并重新分配,直至死锁解除。

    可选择符合下面条件之一的先撤销:

    1. CPU消耗时间最少者

    2. 产生的输出量最小者

    3. 预计剩余执行时间最长者

    4. 分得的资源数量最少者后优先级最低者

  • 系统重启法
    结束所有进程的执行并重新启动操作系统。这种方法很简单,但先前的工作全部作废,损失很大。