取消 IRP 时要考虑的要点

本部分讨论实现 Cancel 例程和处理可取消 IRP 的准则。 有关处理可取消 IRP 的详细信息,请参阅 Cancel-Safe IRP 排队的控制流程

所有取消例程的通用指南

每当调用驱动程序的 Cancel 例程时,I/O 管理器将保留取消旋转锁。 因此,每个 Cancel 例程都必须:

  • 在返回控件之前调用 IoReleaseCancelSpinLock

  • 不调用 IoAcquireCancelSpinLock ,除非它首先调用 IoReleaseCancelSpinLock

  • 每次调用 IoAcquireCancelSpinLock 后,应相应调用 IoReleaseCancelSpinLock

每次 Cancel 例程调用 IoReleaseCancelSpinLock 时,它都必须传递最近调用 IoAcquireCancelSpinLock 返回的 IRQL。 在释放由 I/O 管理器获取并在调用 Cancel 例程时仍持有的旋转锁时,Cancel 例程必须传递 Irp->CancelIrql

在持有自旋锁时,驱动程序不得调用外部例程(如 IoCompleteRequest),否则可能导致死锁。

使用 I/O 管理器定义的队列

除非驱动程序管理其自身的内部 IRP 队列,否则其 Cancel 例程将在传入的 IRP 上调用,该 IRP 可以是以下任一项:

  • 输入目标设备对象中的 CurrentIrp

  • 与目标设备对象关联的设备队列中的条目

除非驱动程序管理自己的 IRP 内部队列,否则其 Cancel 例程应使用输入 IRP 调用 KeRemoveEntryDeviceQueue ,以测试它是否是与目标设备对象关联的设备队列中的条目。 驱动程序的 Cancel 例程 无法 调用 KeRemoveDeviceQueueKeRemoveByKeyDeviceQueue ,因为它无法假定给定的 IRP 位于设备队列中的任何特定位置。

输入 IRP 的当前状态

当对已开始 I/O 处理的 IRP 调用 Cancel 例程,并且请求即将完成时,Cancel 例程应释放系统自旋锁并返回控制。

如果输入 IRP 的当前状态为挂起,则 Cancel 例程必须执行以下操作:

  1. 将输入 IRP 的 I/O 状态块的 Status 设置为 STATUS_CANCELLED “状态”,将 “信息” 设置为零。

  2. 释放它持有的任何旋转锁,包括系统取消自旋锁。

  3. 使用给定的 IRP 调用 IoCompleteRequest

保留处于可取消状态的 IRP

任何处于可取消状态的 IRP 的驱动程序例程都必须调用 IoMarkIrpPending ,并且必须调用 IoSetCancelRoutine 来为 IRP 中的 Cancel 例程设置其入口点。 只有这样,驱动程序例程才能调用其他支持例程,例如IoStartPacketIoAllocateControllerExInterlockedInsert..List例程。

后续处理可取消 IRP 的任何驱动程序例程都必须检查 IRP 是否已在开始作以满足请求之前已取消。 例程必须调用 IoSetCancelRoutine ,才能将 Cancel 例程的入口点重置为 IRP 中的 NULL 。 只有这样,该例程才能开始其输入 IRP 的 I/O 处理。

例程可能需要重置 IRP 中 取消 例程的入口点,如果该例程也将 IRP 传递给其他驱动程序例程进行进一步处理,而那些 IRP 可能被保持在可取消状态。

任何处于可取消状态的 IRP 的较高级别驱动程序都必须将其 “取消” 入口点重置为 NULL ,然后才能使用 IoCallDriver 将 IRP 传递到下一个较低的驱动程序。

取消 IRP

任何更高级别的驱动程序都可以使用已分配并传递的 IRP 调用 IoCancelIrp ,以供较低级别的驱动程序进一步处理。 但是,此类驱动程序不能假定给定的 IRP 会被较低层驱动程序完成,并且状态为 STATUS_CANCELLED。

同步

驱动程序可以(或必须根据其设计)在其设备扩展中维护其他状态信息,以跟踪 IRP 的可取消状态。 如果此状态由在 IRQL <= DISPATCH_LEVEL运行的驱动程序例程共享,则应使用驱动程序分配和初始化的旋转锁来保护共享数据。

驱动程序应仔细管理其对系统取消自旋锁和自身自旋锁的获取和释放。 它应尽量在最短时间内持有系统取消自旋锁。 在访问可取消的 IRP 之前,此类驱动程序应始终检查 IoSetCancelRoutine 的返回值,以确定 Cancel 例程是否已运行(或即将运行):如果是这样,它应该让 Cancel 例程完成 IRP。

如果设备驱动程序维护有关各种驱动程序例程与其 ISR 共享的可取消 IRP 状态信息,那么这些其他例程必须与 ISR 同步对该共享状态的访问。 只有驱动程序提供的 SynchCritSection 例程才能以多处理器安全的方式访问与 ISR 共享的状态信息。

有关详细信息,请参阅 同步技术