设置和使用联锁队列
新驱动程序应优先使用 取消安全的 IRP 队列 框架,而不是本节中概述的方法。
具有设备专用线程的驱动程序或使用执行工作线程的驱动程序(如大多数系统 FSD)是最有可能用于在互锁队列中管理 IRP 的运行时内部队列的驱动程序类型。 所有 PnP 驱动程序(包括 WDM 驱动程序)还必须在进行 PnP 和电源状态转换时在内部将某些 IRP 排队。
通常,这些驱动程序会设置一个双链接的互锁队列;每个 IRP 都包含 LIST_ENTRY 类型的成员,驱动程序可以使用该成员来双重链接当前持有的 IRP。 如果驱动程序设置了单独链接的互锁队列,则无法重新排队重试 IRP。
驱动程序必须在设备初始化时设置其互锁队列。 下图演示了一个双链接互锁队列、驱动程序必须调用才能设置此类队列的支持例程,以及驱动程序可以调用的一组 ExInterlockedXxx 例程,以便将 IRP 插入队列并从队列中删除 IRP。
如图所示,驱动程序必须为队列本身和以下内容提供存储,才能设置双链接互锁队列:
执行旋转锁,驱动程序必须调用 KeInitializeSpinLock 才能初始化该锁。 通常,驱动程序在其 AddDevice 例程中为其设备对象 () 设置设备扩展 () 时,会初始化旋转锁。
队列的列表头,驱动程序必须通过调用 InitializeListHead 对其进行初始化。
大多数使用双链接互锁队列的驱动程序在驱动程序创建的设备对象的设备扩展中提供必要的存储。 如果驱动程序使用控制器对象) 或驱动程序分配的非分页池中,则队列和执行旋转锁可以改为位于 控制器 扩展 (中。
当驱动程序接受 I/O 请求时,如果 ListHead 的类型为 LIST_ENTRY,它可以通过调用以下任一支持例程将 IRP 插入其队列中,如上图所示:
ExInterlockedInsertTailList ,用于将 IRP 放置在队列末尾
ExInterlockedInsertHeadList ,用于将 IRP 放置在队列的前面。 驱动程序通常仅在必须重试特定请求时才调用此例程。
驱动程序必须将指向 IRP (ListEntry) 的指针以及 ListHead 和 executive spin lock (Lock) 指针传递给其中每个 ExInterlockedInsertXxxList 例程。 当驱动程序通过调用 ExInterlockedRemoveHeadList 取消 IRP 排队时,只需要指向 ListHead 和 Lock 的指针。 为防止死锁,驱动程序不得持有传递给任何 ExInterlockedXxx 例程的 ExecutiveSpinLock。
由于互锁队列受执行旋转锁的保护,因此驱动程序可以将 IRP 插入到其双重链接的队列中,并以多处理器安全的方式将其从以小于或等于 IRQL = DISPATCH_LEVEL 运行的任何驱动程序例程中删除。
如上图所示,ListHead 类型 为 LIST_ENTRY 的队列是一个双链接列表。 具有 类型为 SLIST_HEADER 的 ListHead 是一个按顺序单独链接的列表。 驱动程序通过调用 ExInitializeSListHead,初始化序列单锁互锁队列的 ListHead。
从不重试 I/O 操作的驱动程序可以使用 ExInterlockedPushEntrySList 和 ExInterlockedPopEntrySList 在经过排序的单独链接互锁队列中内部管理其 IRP 的队列。 使用此类互锁队列的任何驱动程序还必须为 SLIST_HEADER 类型的 ListHead 和 ExecutiveSpinLock 提供驻留存储,如 上图所示。 在调用 ExInterlockedPushEntrySList 将初始条目插入其队列之前,它必须初始化旋转锁并设置其队列。