1. 概述

在本篇文章中,我们将深入探讨死锁的概念、形成条件以及如何预防、避免、检测和处理死锁。通过实际示例和操作系统层面的分析,帮助你全面掌握这一关键知识点。

2. 死锁简介

死锁是指两个或多个进程在执行过程中,因争夺资源而造成的一种互相等待的僵局。只要进程都在等待对方释放资源,整个系统就无法继续推进。

这种现象在分布式系统中尤为常见。举个简单的例子:

1D

假设有三个进程 P1、P2、P3 和三个资源 R1、R2、R3。

  • P1 请求 R2,但 R2 被 P2 占用;
  • P2 请求 R3,但 R3 被 P3 占用;
  • P3 请求 R1,但 R1 被 P1 占用;

此时,三个进程都在等待对方释放资源,陷入死锁状态。

3. 死锁的四个必要条件

死锁的产生必须同时满足以下四个条件:

互斥(Mutual Exclusion)
资源不能共享,一次只能被一个进程占用。

持有并等待(Hold and Wait)
一个进程在等待其他资源时,不释放已持有的资源。

不可抢占(No Preemption)
资源只能由持有它的进程主动释放,不能被强制剥夺。

循环等待(Circular Wait)
存在一个进程链,每个进程都在等待下一个进程所持有的资源。

只要这四个条件同时成立,就可能发生死锁。

4. 死锁预防

预防死锁的核心思想是破坏上述四个条件中的任意一个。

4.1 破坏互斥

理论上可以允许多个进程共享资源,但现实中很多资源(如打印机、文件锁)无法共享。因此,这个方法不可行。

4.2 破坏“持有并等待”

  • 一次性申请所有资源:进程在启动前申请所有所需资源,避免中途等待。缺点是资源利用率低。
  • 释放已有资源再申请新资源:避免同时持有多个资源。但可能导致资源饥饿(Starvation)

4.3 破坏“不可抢占”

允许系统强制回收资源,比如操作系统可以在进程等待时回收其资源。这在某些场景下可行,但可能影响程序逻辑。

4.4 破坏“循环等待”

给资源编号,强制进程按编号顺序申请资源。例如:

  • 资源 R1 编号为 1,R2 为 2;
  • 进程必须先申请编号较小的资源,再申请大的;
  • 如果申请编号较小的资源,必须先释放当前较大的资源。

这样可以避免循环等待,但增加了程序设计的复杂性。

5. 死锁避免

死锁避免不同于预防,它不强制破坏条件,而是通过系统调度来避开进入不安全状态。

5.1 资源分配图(RAG)

资源分配图是一种图示工具,用于表示进程与资源的分配与请求关系。

  • 顶点:进程节点和资源节点;
  • :请求边和分配边。

示意图如下:

D1

RAG 适用于资源只有一个实例的场景。若资源有多个实例,则无法准确判断是否会发生死锁。

5.2 银行家算法(Banker's Algorithm)

银行家算法用于资源有多个实例的场景,核心思想是:

  • 系统维护一个资源分配矩阵;
  • 在分配资源前,先判断是否会导致系统进入不安全状态;
  • 只有安全状态下才进行资源分配。

示例场景:

  • 两个进程 P1、P2;
  • 两个资源 R1、R2;

2D

系统通过预判,避免形成循环等待,从而防止死锁。

6. 死锁检测与恢复

与预防和避免不同,检测与恢复方法是允许死锁发生,但通过定期检测并采取措施来恢复系统。

6.1 等待图(Wait-for Graph)

等待图是资源分配图的简化版,只包含进程节点和请求关系。

示意图如下:

3D

若图中存在环,则表示系统处于死锁状态。

6.2 安全性算法(Safety Algorithm)

适用于资源有多个实例的情况,与银行家算法类似,但不需要最大资源需求矩阵。

算法维护三个矩阵:

  • 已分配资源矩阵;
  • 可用资源矩阵;
  • 当前需求矩阵。

6.3 恢复策略

✅ 乐观策略(Optimistic Approach)

  • 资源抢占:强制回收某些进程的资源,重新分配;
  • 回滚(Rollback):将系统回退到某个安全状态。

⚠️ 缺点:可能导致进程饥饿或状态不一致。

✅ 悲观策略(Pessimistic Approach)

  • 终止所有死锁进程:最直接但代价最高;
  • 逐个终止进程:逐步恢复,直到死锁解除。

示意图如下:

D3

7. 死锁忽略

在某些系统中,如 Linux 和 Windows,选择忽略死锁问题。系统假设死锁不会发生,一旦发生则直接重启。

这种方式适用于对稳定性要求不高、用户可接受重启的场景。

8. 总结

本文系统性地讲解了死锁的定义、形成条件以及处理策略:

方法 是否允许死锁 优点 缺点
死锁预防 彻底避免 资源利用率低
死锁避免 动态判断 实现复杂
死锁检测与恢复 灵活 恢复代价高
死锁忽略 简单 不可靠

在实际开发中,建议结合业务场景选择合适的策略。对于并发程度高、资源竞争激烈的系统,应优先考虑死锁预防和避免机制;而对于用户级应用,可适当采用检测与忽略策略。


原始标题:Deadlock: What It Is, How to Detect, Handle and Prevent?