设置和使用设备队列

驱动程序通过在驱动程序或设备初始化时调用 KeInitializeDeviceQueue 来设置设备队列对象。 启动其设备 () 后,驱动程序通过调用 KeInsertDeviceQueueKeInsertByKeyDeviceQueue 将 IRP 插入此队列。 下图演示了这些调用。

设置和使用设备队列。

如图所示,驱动程序必须为必须驻留的设备队列对象提供存储。 设置设备队列对象的驱动程序通常在驱动程序创建 的设备对象的设备扩展 中提供必要的存储,但如果驱动程序使用控制器对象或驱动程序分配的非分页池,则存储可以位于 控制器 扩展中。

如果驱动程序为设备扩展中的设备队列对象提供存储,则会在创建设备对象后和启动设备之前调用 KeInitializeDeviceQueue 。 换句话说,驱动程序可以从其 AddDevice 例程或在处理 PnP IRP_MN_START_DEVICE 请求时初始化队列。 在调用 KeInitializeDeviceQueue 时,驱动程序将指针传递给它为设备队列对象提供的存储。

启动其设备 () 后,驱动程序可以通过调用 KeInsertDeviceQueue(将 IRP 置于队列末尾的 KeInsertByKeyDeviceQueue)或 KeInsertByKeyDeviceQueue(根据驱动程序确定的 SortKey 值将 IRP 放入队列)将 IRP 插入到其设备队列中,如上图所示。

其中每个支持例程都返回一个布尔值,该值指示 IRP 是否已插入队列。 如果队列当前为空,则其中每个调用还会将设备队列对象的状态设置为 Busy (Not-Busy) 。 但是,如果队列为空 (非繁忙) , 则 KeInsertXxxDeviceQueue 例程都不会将 IRP 插入队列。 相反,它将设备队列对象的状态设置为 Busy,并返回 FALSE。 由于 IRP 尚未排队,因此驱动程序必须将它传递给另一个驱动程序例程,以便进一步处理。

设置补充设备队列时,请遵循以下实现准则:

当对 KeInsertXxxDeviceQueue 的调用返回 FALSE 时,调用方必须将它尝试排队的 IRP 传递到另一个驱动程序例程进行进一步处理。 但是,调用 KeInsertXxxDeviceQueue 会将设备队列对象的状态更改为 Busy,因此,除非驱动程序先调用 KeRemoveXxxDeviceQueue ,否则将插入队列中的下一个 IRP。

当设备队列对象的状态设置为“忙碌”时,驱动程序可以通过调用以下支持例程之一将 IRP 取消排队以进一步处理或将状态重置为Not-Busy:

调用上述任何例程以从设备队列中删除空但“忙碌”的条目,将队列状态更改为“不忙”。

每个设备队列对象都受内置的执行旋转锁保护, (未显示在 “使用设备队列对象” 图) 中。 因此,驱动程序可以将 IRP 插入队列,并以多处理器安全的方式从以小于或等于 IRQL = DISPATCH_LEVEL 运行的任何驱动程序例程中删除它们。 由于此 IRQL 限制,驱动程序无法从在 DIRQL 上运行的 ISR 或 SynchCritSection 例程调用任何 KeXxxDeviceQueue 例程。

有关详细信息 ,请参阅管理硬件优先级旋转锁 。 有关特定支持例程的 IRQL 要求,请参阅例程的参考页。