快速互斥锁和受保护互斥锁

从 Windows 2000 开始,驱动程序在需要低开销互斥锁时,可以使用 快速互斥体,以便在 IRQL <= APC_LEVEL 上执行的代码实现互斥。 快速互斥体可以保护一次只能由一个线程输入的代码路径。 若要输入受保护的代码路径,线程 将获取 互斥体。 如果另一个线程已获取互斥体,则在释放互斥体之前,当前线程的执行将暂停。 若要退出受保护的代码路径,线程 将释放 互斥体。

从 Windows Server 2003 开始,驱动程序还可以使用 保护互斥锁。 受保护的互斥体是快速互斥体的直接替代品,同时提供更好的性能。 与快速互斥体一样,受保护的互斥体可以保护一个代码路径,该路径一次只能由一个线程输入。 但是,使用受保护的互斥体的代码比使用快速互斥体的代码更快地运行。

在 Windows 8 之前的 Windows 版本中,受保护的互斥锁的实现方式与快速互斥锁不同。 由快速互斥体保护的代码路径在 IRQL = APC_LEVEL 运行。 由受保护的互斥锁保护的代码路径在 IRQL <= APC_LEVEL 下运行,但禁用了所有 APC。 在这些早期版本的 Windows 中,获取受保护的互斥体比获取快速互斥体更快。 但是,这两种类型的互斥体的行为相同,并受到相同的限制。 特别是,在 IRQL = APC_LEVEL 时,非法调用的内核例程不应从由快速互斥体或受保护的互斥体保护的代码路径中调用。

从 Windows 8 开始,受保护的互斥体实现为快速互斥体。 在受受保护的互斥体或快速互斥体保护的代码路径中, 驱动程序验证程序 将内核例程的调用视为在 IRQL = APC_LEVEL 发生的。 与早期版本的 Windows 一样,APC_LEVEL的调用在受受保护的互斥体或快速互斥体保护的代码路径中是非法的。

快速互斥体

快速互斥体由 FAST_MUTEX 结构表示。 驱动程序为 FAST_MUTEX 结构分配自己的存储,然后调用 ExInitializeFastMutex 例程来初始化结构。

线程通过执行以下之一获取快速互斥体:

  • 调用 ExAcquireFastMutex 例程。 如果互斥体已被另一个线程获取,调用线程的执行将暂停,直到该互斥体可用。

  • 调用 ExTryToAcquireFastMutex 例程以尝试获取快速互斥体,而不挂起当前线程。 无论是否已获取互斥体,函数都会立即返回。 如果成功为调用者获取互斥体,ExTryToAcquireFastMutex 将返回 TRUE;否则,将返回 FALSE

线程调用 ExReleaseFastMutex 以释放由 ExAcquireFastMutexExTryToAcquireFastMutex 获取的快速互斥体。

受快速互斥体保护的代码路径在 IRQL = APC_LEVEL 时运行。 ExAcquireFastMutexExTryToAcquireFastMutex 将当前 IRQL 提升为 APC_LEVEL,ExReleaseFastMutex 将还原原始 IRQL。 因此,当线程保存快速互斥体时,将禁用所有 APC。

如果保证代码路径始终在APC_LEVEL运行,驱动程序可以改为调用 ExAcquireFastMutexUnsafeExReleaseFastMutexUnsafe 来获取和释放快速互斥体。 这些例程不会更改当前的 IRQL,并且只有在当前 IRQL 为 APC_LEVEL 时才能安全使用。

无法以递归方式获取快速互斥体。 如果一个已持有快速互斥锁的线程尝试再次获取它,该线程将陷入死锁。 快速互斥体只能在 IRQL <= APC_LEVEL 运行的代码中使用。

受保护的互斥体

从 Windows Server 2003 开始提供的受保护的互斥体执行与快速互斥体相同的功能,但性能更高。

从 Windows 8 开始,受保护的 mutex 和快速 mutex 实现方式相同。

在 Windows 8 之前的 Windows 版本中,受保护的互斥体与快速互斥体不同。 获取快速互斥体会将当前的 IRQL 提升为APC_LEVEL,而获取受保护的互斥体会使其进入一个受保护的区域,这是一种更快的操作。 有关受保护的区域的详细信息,请参阅 关键区域和受保护的区域

受保护的互斥体由 KGUARDED_MUTEX 结构表示。 驱动程序为 KGUARDED_MUTEX 结构分配自己的存储,然后调用 KeInitializeGuardedMutex 例程来初始化结构。

线程通过以下其中一种方式获取加锁保护的互斥体:

  • 调用 KeAcquireGuardedMutex。 如果该互斥体已被另一个线程获取,则调用该互斥体的线程的执行将被挂起,直到该互斥体变为可用。

  • 调用 KeTryToAcquireGuardedMutex 以尝试获取受保护的互斥体,而不挂起当前线程。 无论是否已获取互斥体,例程都会立即返回。 如果为调用方成功获取互斥体,KeTryToAcquireGuardedMutex 返回 TRUE;否则,返回 FALSE

线程调用 KeReleaseGuardedMutex 以释放通过 KeAcquireGuardedMutexKeTryToAcquireGuardedMutex 获取的保护互斥体。

保留受保护的互斥体的线程隐式在受保护的区域中运行。 KeAcquireGuardedMutexKeTryToAcquireGuardedMutex 进入受保护的区域,而 KeReleaseGuardedMutex 将退出它。 当线程持有受保护的互斥锁时,所有 APC 将被禁用。

如果保证在禁用所有 APC 的情况下运行代码路径,驱动程序可以改用 KeAcquireGuardedMutexUnsafeKeReleaseGuardedMutexUnsafe 进行受保护的互斥体的获取和释放。 这些例程不会进入或退出受保护的区域,并且只能在已存在的受保护的区域或 IRQL = APC_LEVEL内使用。

无法以递归方式获取受保护的互斥体。 如果已持有受保护的互斥体的线程尝试获取它,该线程将死锁。 受保护的互斥体只能在 IRQL <= APC_LEVEL 运行的代码中使用。