互斥对象简介

顾名思义,互斥体对象是一种同步机制,旨在确保对一组内核模式线程之间共享的单个资源的互斥访问。 只有使用执行工作线程的文件系统驱动程序(如文件系统驱动程序 (FSD) )可能会使用互斥体对象。

可能,具有驱动程序创建的线程或辅助线程回调例程的最高级别驱动程序可能使用互斥体对象。 但是,任何具有可分页线程或工作线程回调例程的驱动程序都必须非常小心地管理其互斥对象获取、等待和释放。

互斥对象具有内置功能,仅提供系统 (内核模式) 线程相互排斥、无锁访问 SMP 计算机中的共享资源。 内核一次将互斥体所有权分配给单个线程。

获取互斥体所有权可防止 (API) 传递正常的内核模式异步过程调用。 除非内核发出APC_LEVEL软件中断以运行特殊内核 APC(例如 I/O 管理器的 IRP 完成例程)将结果返回到 I/O 操作的原始请求者,否则该线程不会被 APC 抢占。

线程可以获取已拥有 (递归所有权的互斥对象的所有权) ,但在线程完全释放所有权之前,递归获取的互斥对象不会设置为 Signaled 状态。 此类线程必须显式释放互斥体,因为它获取所有权的次数,然后另一个线程才能获取互斥体。

内核绝不允许拥有互斥体的线程导致转换到用户模式,而无需先释放互斥体并将其设置为 Signaled 状态。 如果拥有互斥体的任何 FSD 创建或驱动程序创建的线程在释放互斥体所有权之前尝试将控制权返回到 I/O 管理器,则内核会关闭系统。

使用互斥对象的任何驱动程序必须在等待或释放互斥对象之前调用 KeInitializeMutex 一次。 下图演示了两个系统线程如何使用互斥体对象。

diagram illustrating waiting for a mutex object.

如上图所示,使用互斥对象的驱动程序必须提供互斥对象(必须驻留)的存储。 驱动程序可以使用驱动程序创建 的设备对象的设备扩展 、控制器扩展(如果使用 控制器对象)或驱动程序分配的非分页池。

当驱动程序调用 KeInitializeMutex (通常从 其 AddDevice 例程) 时,它必须传递指向驱动程序存储的指针,以便内核初始化为 Signaled 状态。

在初始化此类高级驱动程序后,它可以管理对共享资源的互斥访问,如上图所示。 例如,驱动程序的调度例程用于固有同步操作和线程可能会使用互斥体来保护为 IRP 创建的驱动程序创建的队列。

由于 KeInitializeMutexalways 将互斥对象的初始状态设置为 Signaled (,因为上图显示了) :

  1. 调度例程对 KeWaitForSingleObject 的初始调用与 Mutex 指针将当前线程立即置于就绪状态,为互斥体提供线程所有权,并将互斥体状态重置为 Not-Signaled。 调度例程恢复运行后,就可以安全地将 IRP 插入互斥保护的队列。

  2. 当第二个线程 (另一个调度例程、驱动程序提供的辅助线程回调例程或驱动程序创建的线程) 使用 Mutex 指针调用 KeWaitForSingleObject 时,第二个线程将置于等待状态。

  3. 调度例程按照步骤 1 中所述完成 IRP 排队时,它会使用 Mutex 指针和布尔等待值调用 KeReleaseMutex,该值指示它是否打算在 KeReleaseMutex 返回控件后立即调用 KeWaitForSingleObject (或 KeWaitForMutexObject) 。

  4. 假设调度例程在步骤 3 (Wait 设置为 FALSE) 中释放了互斥体的所有权,则互斥体设置为 KeReleaseMutex 的 Signaled 状态。 互斥体当前没有所有者,因此内核确定另一个线程是否正在等待该互斥体。 如果是这样,内核使第二个线程 (看到步骤 2) 互斥表达式所有者,可能会将线程的优先级提升到最低实时优先级值,并将其状态更改为就绪。

  5. 内核在处理器可用后立即调度第二个线程执行:也就是说,当没有其他优先级较高的线程处于就绪状态并且没有内核模式例程在更高的 IRQL 上运行时。 第二个线程 (调度例程队列 IRP 或驱动程序的工作线程回调例程或驱动程序创建的线程取消排队 IRP) 现在可以安全地访问受互斥保护的 IRP 队列,直到调用 KeReleaseMutex

如果线程以递归方式获取互斥对象的所有权,该线程必须多次显式调用 KeReleaseMutex ,才能将互斥对象设置为 Signaled 状态。 例如,如果线程调用 KeWaitForSingleObject ,然后使用同一 Mutex 指针调用 KeWaitForMutexObject ,则在获取 mutex 时必须调用 KeReleaseMutex 两次才能将该互斥对象设置为 Signaled 状态。

使用 Wait 参数设置为 TRUE 调用 KeReleaseMutex 表示调用方打算在 KeReleaseMutex 返回时立即调用 KeWaitXxx 支持例程。

考虑将 Wait 参数设置为 KeReleaseMutex 的以下准则:

在 IRQL PASSIVE_LEVEL中运行的可分页线程或可分页驱动程序例程不应调用将 Wait 参数设置为 TRUEKeReleaseMutex。 如果调用方碰巧在调用 KeReleaseMutexKeWaitXxxObject (s) 之间分页,则此类调用会导致错误页错误。

任何在 IRQL 上运行且大于 PASSIVE_LEVEL 的标准驱动程序例程都无法等待任何调度程序对象的非零间隔,而不会降低系统。 但是,如果此类例程在 IRQL 上运行的互斥表达式小于或等于DISPATCH_LEVEL,则可以调用 KeReleaseMutex

有关运行标准驱动程序例程的 IRQL 的摘要,请参阅 管理硬件优先级