死锁检测

死锁检测工具监视驱动程序对需要锁定的资源的使用,包括旋转锁、互斥锁和快速互斥锁。 此驱动程序验证程序选项将检测可能在未来某个时候导致死锁的代码逻辑。

驱动程序验证程序的死锁检测选项以及 !死锁 内核调试器扩展是一种有效的工具,用于确保代码避免使用这些资源不当。

死锁检测仅在 Windows XP 和更高版本的 Windows 中受支持。

死锁的原因

当两个或多个线程在某些资源上发生冲突时,会导致 死锁 ,这样就不可能执行。

当两个或多个线程等待另一个线程拥有的资源时,会出现最常见的死锁形式。 如下所示:

线程 1 线程 2
获取锁 A 获取锁 B
请求获取锁 B 请求锁定 A

如果这两个序列同时发生,线程 1 将永远不会获得锁 B,因为它由线程 2 拥有,线程 2 永远不会获得 Lock A,因为它由线程 1 拥有。 这最多会导致涉及的线程停止,最坏的情况会导致系统停止响应。

死锁不限于两个线程和两个资源。 三个线程和三个锁之间的三向死锁是常见的;甚至偶尔会发生五个或六个部分的死锁。 这些死锁在很大程度上依赖于“坏运气”,因为它们需要多个事件同时发生。 然而,获取锁的时间间隔越长,发生锁竞争的可能性就越大。

当线程尝试获取它已拥有的锁时,可能会发生单线程死锁。

所有死锁的共同原因是未遵循锁层次结构。 每当需要一次获取多个锁时,每个锁都应具有明确的优先级。 如果在某一点 A 发生在 B 之前,而在另外一点 B 发生在 C 之前,则层次结构为 A-B-C。 这意味着,A 在 B 或 C 之后不得获取,B 不得在 C 之后获取。

即使没有死锁的可能性,也应该遵循锁层次结构,因为在维护代码的过程中,很容易意外引入死锁。

可能导致死锁的资源

最明确的死锁是 拥有 的资源的结果。 其中包括自旋锁、互斥锁、快速互斥锁和 ERESOURCE。

发出信号而不是通过获取来使用的资源(比如事件和 LPC 端口)往往会更容易引发模棱两可的死锁问题。 当然,如果代码滥用这些资源,这当然是可能的,而且太常见了,这样两个线程最终将无限期地等待对方完成。 但是,由于这些资源实际上并非由任何一个线程所有,因此无法识别具有任何确定度的不公线程。

驱动程序验证工具的死锁检测选项查找涉及旋转锁、互斥体和快速互斥体的潜在死锁。 它不监视 ERESOURCE 的使用,也不会监视非所有者资源的使用。

死锁检测的影响

驱动程序验证程序的死锁检测例程查找不一定同时发生的锁层次结构冲突。 大多数情况下,这些违规会识别出在特定条件下可能发生死锁的代码路径。

若要查找潜在的死锁,驱动程序验证程序会生成资源获取顺序图并检查循环。 如果为每个资源创建一个节点,并在每当一个锁在另一个锁之前被获取时绘制一条箭头,那么路径环路将表示锁层次结构违规。

发现其中一个冲突时,驱动程序验证程序将发出 bug 检查。 在发生任何实际死锁之前,将发生这种情况。

注释

即使冲突的代码路径永远无法同时发生,如果它们涉及锁层次结构冲突,它们仍应重写。 此类代码是一种随时可能发生的潜在死锁,如果代码稍作改动,可能导致真正的死锁。

当死锁检测发现冲突时,它将发出 bug 检查0xC4。 此错误检查的第一个参数将指示确切的违规。 可能的违规包括:

  • 涉及两个或多个线程的锁层次结构违反

  • 试图独占获取自身已为共享所有者的资源的线程(独占拥有的资源可以共享获取;共享资源不能独占获取)。

  • 尝试两次获取同一资源的线程(自锁死)

  • 在未首先获取的情况下发布的资源

  • 由不同于获取它的线程释放的资源

  • 多次初始化或尚未初始化的资源

  • 在仍拥有资源的同时删除的线程

  • 从 Windows 7 开始,驱动程序验证程序可以预测可能的死锁。 例如,尝试将相同的KSPIN_LOCK数据结构用作常规旋转锁和堆栈排队旋转锁。

有关错误检查参数的列表,请参阅 错误检查 0xC4(DRIVER_VERIFIER_DETECTED_VIOLATION)。

监视死锁检测

死锁检测发现冲突后, 可以使用 !deadlock 内核调试器扩展来准确调查所发生的情况。 它可以在最初获取锁时显示锁层次结构拓扑以及每个线程的调用堆栈。

Windows 程序包的调试工具文档中提供了 !死锁 扩展的详细示例,以及有关调试器扩展的一般信息。 有关详细信息,请参阅 Windows 调试

激活此选项

注释

此选项与 内核同步延迟模糊不兼容

可以使用驱动验证管理器或 Verifier.exe 命令行为一个或多个驱动程序激活死锁检测功能。 有关详细信息,请参阅 “选择驱动程序验证程序选项”。

  • 在命令行

    在命令行中,死锁检测选项由 位 5 (0x20) 表示。 若要激活死锁检测,请使用0x20标志值或向标志值添加0x20。 例如:

    verifier /flags 0x20 /driver MyDriver.sys
    

    下一次启动后,该功能将处于活动状态。

    在 Windows Vista 和更高版本的 Windows 上,还可以通过在命令中添加 /volatile 参数来激活和停用死锁检测,而无需重新启动计算机。 例如:

    verifier /volatile /flags 0x20 /adddriver MyDriver.sys
    

    此设置立即生效,但在关闭或重新启动计算机时会丢失。 有关详细信息,请参阅 “使用易失性设置”。

    死锁检测功能也包含在标准设置中。 例如:

    verifier /standard /driver MyDriver.sys
    
  • 使用驱动程序验证管理器

    1. 选择 “创建自定义设置”(面向代码开发人员), 然后选择“ 下一步”。

    2. 从完整列表中选择单个设置

    3. 选择(检查) 死锁检测

死锁检测功能也包含在标准设置中。 若要使用此功能,请在 驱动程序验证程序管理器中选择“ 创建标准设置”。