互斥对象简介
顾名思义,互斥对象是一种同步机制,旨在确保对一组内核模式线程之间共享的单个资源进行互斥访问。 只有使用执行工作线程的高级驱动程序(例如文件系统驱动程序 (FSD) )才可能使用互斥对象。
可能,具有驱动程序创建的线程或工作线程回调例程的最高级别驱动程序可能会使用互斥对象。 但是,任何具有可分页线程或工作线程回调例程的驱动程序都必须非常小心地管理其互斥对象的获取、等待和发布。
互斥对象具有内置功能,仅) 线程以互斥、无死锁方式访问 SMP 计算机中的共享资源,提供系统 (内核模式。 内核一次将互斥体的所有权分配给单个线程。
获取互斥体的所有权会阻止 (APC) 传递正常的内核模式异步过程调用。 除非内核发出APC_LEVEL软件中断以运行特殊内核 APC(例如 I/O 管理器的 IRP 完成例程,该例程将结果返回到 I/O 操作的原始请求者)时,线程不会被 APC 抢占
线程可以获取它已拥有的互斥对象 (递归所有权) ,但在线程完全释放其所有权之前,递归获取的互斥对象不会设置为 Signaled 状态。 此类线程必须在获得所有权后多次显式释放互斥体,然后另一个线程才能获取互斥体。
内核绝不允许拥有互斥锁的线程在不首先释放互斥体并将其设置为“已信号”状态的情况下转换到用户模式。 如果拥有互斥体的任何 FSD 创建或驱动程序创建的线程在释放互斥体的所有权之前尝试将控制权返回到 I/O 管理器,则内核会关闭系统。
任何使用互斥对象的驱动程序都必须调用 KeInitializeMutex 一次,然后才能等待或释放其互斥对象。 下图演示了两个系统线程如何使用互斥体对象。
如上图所示,使用互斥对象的驱动程序必须为互斥对象提供存储,该互斥对象必须是驻留的。 驱动程序可以使用驱动程序创建 的设备对象的设备扩展 、控制器扩展(如果它使用 控制器对象)或驱动程序分配的非分页池。
当驱动程序调用 KeInitializeMutex (通常从其 AddDevice 例程) 时,它必须将指向驱动程序存储的指针传递给互斥对象,内核会将其初始化为 Signaled 状态。
在这样一个最高级别的驱动程序初始化后,它可以管理对共享资源的互斥访问,如上图所示。 例如,用于固有同步操作和线程的驱动程序调度例程可能会使用互斥体来保护驱动程序创建的 IRP 队列。
由于 KeInitializeMutex始终将互斥对象的初始状态设置为 Signaled (如上图所示) :
调度例程使用 Mutex 指针对 KeWaitForSingleObject 进行的初始调用会将当前线程立即进入就绪状态,授予互斥锁的线程所有权,并将互斥状态重置为 Not-Signaled。 调度例程恢复运行后,就可以安全地将 IRP 插入互斥保护的队列中。
当第二个线程 (另一个调度例程、驱动程序提供的工作线程回调例程或驱动程序创建的线程) 使用 Mutex 指针调用 KeWaitForSingleObject 时,第二个线程将进入等待状态。
当调度例程按照步骤 1 中所述完成对 IRP 的排队时,它会使用 Mutex 指针和布尔等待值调用 KeReleaseMutex,该值指示它打算在 KeReleaseMutex 返回控件后立即调用 KeWaitForSingleObject (还是 KeWaitForMutexObject) 。
假设调度例程在步骤 3 中释放了互斥体的所有权, (Wait 设置为 FALSE) ,则 KeReleaseMutex 将互斥体设置为 Signaled 状态。 互斥体当前没有所有者,因此内核确定另一个线程是否正在等待该互斥体。 如果是这样,内核会使第二个线程 () 互斥体所有者看到步骤 2,可能会将线程的优先级提升到最低实时优先级值,并将其状态更改为就绪。
当处理器可用时,内核会立即调度第二个线程以供执行:也就是说,当前没有其他具有更高优先级的线程处于就绪状态,并且没有内核模式例程可在更高的 IRQL 上运行。 第二个线程 (调度例程排队 IRP 或驱动程序的工作线程回调例程或驱动程序创建的线程取消排队 IRP) 现在可以安全地访问受互斥保护的 IRP 队列,直到它调用 KeReleaseMutex。
如果线程以递归方式获取互斥对象的所有权,则该线程必须显式调用 KeReleaseMutex 的次数与它在互斥上等待的次数一样多,以便将互斥对象设置为 Signaled 状态。 例如,如果线程使用同一 Mutex 指针调用 KeWaitForSingleObject,然后调用 KeWaitForMutexObject,则在获取 mutex 时,它必须调用 KeReleaseMutex 两次,以便将互斥对象设置为 Signaled 状态。
使用 Wait 参数设置为 TRUE 调用 KeReleaseMutex 表示调用方打算在从 KeReleaseMutex 返回时立即调用 KeWait Xxx 支持例程。
对于将 Wait 参数设置为 KeReleaseMutex,请考虑以下准则:
在 IRQL PASSIVE_LEVEL运行的可分页线程或可分页驱动程序例程不应调用将 Wait 参数设置为 TRUE 的 KeReleaseMutex。 如果调用方恰好在 调用 KeReleaseMutex 和 KeWaitXxx对象 () 之间分页,则此类调用会导致严重页面错误。
在大于 PASSIVE_LEVEL 的 IRQL 上运行的任何标准驱动程序例程都不能等待任何调度程序对象的非零间隔,而不会关闭系统。 但是,如果此类例程在 IRQL 小于或等于 DISPATCH_LEVEL 运行时拥有互斥体,则可以调用 KeReleaseMutex 。
有关运行标准驱动程序例程的 IRQL 的摘要,请参阅 管理硬件优先级。