内核调度程序对象简介
内核定义一组称为 内核调度程序对象的对象类型,或仅 定义调度程序对象。 Dispatcher 对象包括计时器对象、事件对象、信号灯对象、互斥对象和线程对象。
驱动程序可以将调度程序对象用作非比特线程上下文中的同步机制,同时在 IRQL 执行时等于PASSIVE_LEVEL。
Dispatcher 对象状态
每个内核定义的调度程序对象类型都有一个状态,该状态要么设置为 Signaled,要么设置为 Not-Signaled。
如果一个或多个线程调用 KeWaitForSingleObject、KeWaitForMutexObject 或 KeWaitForMultipleObjects,则一组线程可以同步其操作。 这些函数将调度程序对象指针作为输入并等待,直到另一个例程或线程将一个或多个调度程序对象设置为“已信号”状态。
当线程调用 KeWaitForSingleObject 以等待调度程序对象 (或 KeWaitForMutexObject 以获取互斥) 时,线程将进入 等待 状态,直到调度程序对象设置为 Signaled 状态。 线程可以调用 KeWaitForMultipleObjects 来等待一组调度程序对象的任意或全部设置为 Signaled。
每当调度程序对象设置为“已发出信号”状态时,内核就会更改等待该对象 就绪的任何线程的状态。 (同步计时器和同步事件是此规则的例外情况;当向同步事件或计时器发出信号时,只会将一个等待线程设置为就绪状态。有关详细信息,请参阅 计时器对象和 DPC 和 事件对象。) 处于就绪状态的线程将根据其当前运行时 线程优先级 和具有该优先级的任何线程的处理器的当前可用性来计划运行。
驱动程序何时可以等待 Dispatcher 对象?
通常,仅当以下至少一种情况为 true 时,驱动程序才能等待调度程序对象设置:
驱动程序在非比特线程上下文中执行。
也就是说,可以标识将进入等待状态的线程。 实际上,在非比特线程上下文中执行的唯一驱动程序例程是任何驱动程序的 DriverEntry、 AddDevice、 Reinitialize 和 Unload 例程,以及最高级别驱动程序的调度例程。 所有这些例程都由系统直接调用。
驱动程序正在执行完全同步的 I/O 请求。
也就是说,在处理 I/O 请求时,没有驱动程序会将任何操作排入队列,并且直到其下面的驱动程序处理完请求,否则不会返回任何驱动程序。
此外,如果驱动程序在 IRQL 或高于等于 DISPATCH_LEVEL 执行,则无法进入等待状态。
根据这些限制,必须使用以下规则:
任何 驱动程序的 DriverEntry、 AddDevice、 Reinitialize 和 Unload 例程都可以等待调度程序对象。
最高级别驱动程序的调度例程可以等待调度程序对象。
如果 I/O 操作是同步的,则较低级别驱动程序的调度例程可以等待调度对象,例如创建、刷新、关闭和关闭操作、某些设备 I/O 控制操作以及某些 PnP 和电源操作。
较低级别驱动程序的调度例程不能等待调度程序对象完成异步 I/O 操作。
在 IRQL DISPATCH_LEVEL或更高位置执行的驱动程序例程不得等待调度程序对象设置为“已信号”状态。
驱动程序不得尝试等待调度程序对象设置为信号状态,以便完成与分页设备的传输操作。
为读/写请求提供服务的驱动程序调度例程通常不能等待调度程序对象设置为“已信号”状态。
仅当 I/O 控制代码的传输类型METHOD_BUFFERED时,设备 I/O 控制请求的调度例程才能等待调度程序对象设置为“已信号”状态。
SCSI 微型端口驱动程序不应使用内核调度程序对象。 SCSI 微型端口驱动程序应仅调用 SCSI 端口库例程。
所有其他标准驱动程序例程在任意线程上下文中执行:调用驱动程序例程处理排队操作或处理设备中断时,任何线程的线程都恰好是当前线程。 此外,大多数标准驱动程序例程在引发的 IRQL 中运行,无论是在 DISPATCH_LEVEL,还是对于设备驱动程序,在 DIRQL。
如有必要,驱动程序可以创建设备专用线程,该线程可以等待驱动程序的其他例程 (,但 ISR 或 SynchCritSection 例程除外,) 将调度程序对象设置为信号状态并重置为Not-Signaled状态。
一般指导原则是,如果预计新设备驱动程序在 I/O 操作期间等待设备状态更改时通常需要停止超过 50 微秒,请考虑使用设备专用线程实现驱动程序。 如果设备驱动程序也是最高级别的驱动程序,请考虑使用 系统工作线程 并实现一个或多个工作线程回调例程。 请参阅 PsCreateSystemThread 和使用 Driver-Created线程管理互锁队列。