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

从 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开始,受保护的互斥体和快速互斥体的实现方式相同。

在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 下运行的代码中使用。