管理硬件优先级

执行驱动程序例程的 IRQL 确定它可以调用的内核模式驱动程序支持例程。 例如,某些驱动程序支持例程要求调用方在 IRQL = DISPATCH_LEVEL 运行。 如果调用方在高于 PASSIVE_LEVEL 的任何 IRQL 下运行,则无法安全地调用其他调用。

下面是调用最常实现的标准驱动程序例程的 IRQL 列表。 IDL 按从低到高优先级列出。

PASSIVE_LEVEL
中断屏蔽关闭 — 无。

在 调用的驱动程序例程 PASSIVE_LEVEL — DriverEntryAddDeviceReinitializeUnload 例程、大多数调度例程、驱动程序创建的线程、工作线程回调。

APC_LEVEL
中断屏蔽关闭 - APC_LEVEL中断被屏蔽。

在 调用的驱动程序例程 APC_LEVEL — 某些调度例程 (请参阅 调度例程和 IRQL) 。

DISPATCH_LEVEL
屏蔽关闭的中断 - DISPATCH_LEVEL中断和APC_LEVEL中断被屏蔽。 可能会发生设备、时钟和电源故障中断。

在 调用的驱动程序例程 DISPATCH_LEVEL — StartIoAdapterControlAdapterListControlControllerControlIoTimerCancel (同时持有取消旋转锁) 、 DpcForIsrCustomTimerDpcCustomDpc 例程。

DIRQL
中断屏蔽关闭 — IRQL <处的所有中断 = 驱动程序中断对象的 DIRQL。 可能会发生具有较高 DIRQL 值的设备中断,以及时钟和电源故障中断。

在 DIRQL 中调用的驱动程序例程 — InterruptServiceSynchCritSection 例程。

APC_LEVEL与PASSIVE_LEVEL的唯一区别是,在APC_LEVEL执行的进程无法获得 APC 中断。 但两个 IRQL 都表示线程上下文,两者都意味着代码可以分页。

最低级别的驱动程序在运行三个 IRQL 之一时处理 IRP:

  • PASSIVE_LEVEL,在驱动程序的 Dispatch 例程 () ,处理器上没有屏蔽中断

    DriverEntryAddDeviceReinitializeUnload 例程也PASSIVE_LEVEL运行,驱动程序创建的任何系统线程也是如此。

  • DISPATCH_LEVEL, 在 StartIo 例程中,处理器上屏蔽了DISPATCH_LEVEL和APC_LEVEL中断

    AdapterControlAdapterListControlControllerControlIoTimerCancel (同时保留取消旋转锁) , CustomTimerDpc 例程也在DISPATCH_LEVEL运行, DpcForIsrCustomDpc 例程也是如此。

  • 设备 IRQL (DIRQL) ,在 ISR 和 SynchCritSection 例程中,所有中断都小于或等于驱动程序中断对象的 SynchronizeIrql () 在处理器上屏蔽

大多数较高级别的驱动程序在以下两个 IRQL 中运行时处理 IRP:

  • PASSIVE_LEVEL,在驱动程序的调度例程中,处理器上没有屏蔽中断

    DriverEntryReinitializeAddDeviceUnload 例程也PASSIVE_LEVEL运行,任何驱动程序创建的系统线程、工作线程回调例程或文件系统驱动程序也是如此。

  • DISPATCH_LEVEL,在驱动程序的 IoCompletion 例程 () ,处理器上屏蔽了DISPATCH_LEVEL和APC_LEVEL中断

    IoTimerCancelCustomTimerDpc 例程也在DISPATCH_LEVEL运行。

在某些情况下,在 IRQL APC_LEVEL调用大容量存储设备的中间和最低级别驱动程序。 具体而言,这可能发生在页面错误时,文件系统驱动程序将 IRP_MJ_READ 请求发送到较低级别的驱动程序。

大多数标准驱动程序例程在 IRQL 中运行,该 IRQL 允许它们仅调用相应的支持例程。 例如,设备驱动程序必须在 IRQL DISPATCH_LEVEL运行时调用 AllocateAdapterChannel 。 由于大多数设备驱动程序从 StartIo 例程调用这些例程,因此它们通常已在DISPATCH_LEVEL运行。

请注意,没有 StartIo 例程的设备驱动程序,因为它设置和管理自己的 IRP 队列,在应调用 AllocateAdapterChannel 时,不一定在 DISPATCH_LEVEL IRQL 上运行。 此类驱动程序必须在调用 KeRaiseIrql 和 KeLowerIrql 之间嵌套对 AllocateAdapterChannel 的调用,以便在调用 AllocateAdapterChannel 时在所需的 IRQL 上运行,并在调用例程重新获得控制权时还原原始 IRQL。

调用驱动程序支持例程时,请注意以下事项。

  • 使用小于当前 IRQL 的输入值调用 KeRaiseIrql 会导致严重错误。 调用 KeLowerIrql 只是为了还原原始 IRQL (即,在调用 KeRaiseIrql 后,) 也会导致严重错误。

  • 在 IRQL >= DISPATCH_LEVEL 运行时,为内核定义的调度程序对象调用 KeWaitForSingleObjectKeWaitForMultipleObjects 以等待非零间隔会导致致命错误。

  • 可以安全地等待事件、信号灯、互斥体或计时器设置为信号状态的唯一驱动程序例程是在 IRQL PASSIVE_LEVEL的非比特线程上下文中运行的例程,例如驱动程序创建的线程、 DriverEntryReinitialize 例程,或调度固有同步 I/O 操作 (例程(如大多数设备 I/O 控制请求) )。

  • 即使在 IRQL PASSIVE_LEVEL运行时,可分页驱动程序代码也不得调用将输入 Wait 参数设置为 TRUEKeSetEventKeReleaseSemaphoreKeReleaseMutex。 此类调用可能会导致严重的页面错误。

  • 以大于 IRQL APC_LEVEL 运行的任何例程既不能从分页池分配内存,也不能安全地访问分页池中的内存。 如果 IRQL 中运行的例程大于 APC_LEVEL 导致页面错误,则这是一个致命错误。

  • 调用 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel,驱动程序必须在 IRQL DISPATCH_LEVEL上运行。

    驱动程序在调用 KeAcquireSpinLock 时,可以在 IRQL <= DISPATCH_LEVEL 运行,但它必须通过调用 KeReleaseSpinLock 释放该旋转锁。 换句话说,通过调用 KeReleaseSpinLockFromDpcLevel 释放使用 KeAcquireSpinLock 获取的旋转锁是一个编程错误。

    驱动程序不得在 IRQL > DISPATCH_LEVEL运行时调用 KeAcquireSpinLockAtDpcLevelKeReleaseSpinLockFromDpcLevelKeAcquireSpinLockKeReleaseSpinLock

  • 调用使用自旋锁的支持例程(如 ExInterlockedXxx 例程)会在当前处理器上引发 IRQL,以DISPATCH_LEVEL,如果调用方尚未在引发的 IRQL 下运行,则会引发 DIRQL。

  • 在 IRQL > PASSIVE_LEVEL 运行的驱动程序代码应尽快执行。 运行例程的 IRQL 越高,优化该例程以尽快执行该例程对于良好的整体性能就越重要。 例如,调用 KeRaiseIrql 的任何驱动程序都应尽快对 KeLowerIrql 进行倒数调用。

有关确定优先级的详细信息,请参阅 计划、线程上下文和 IRQL 白皮书。