设备扩展

对于大多数中间和最低级别的驱动程序,设备扩展是与设备对象关联的最重要的数据结构。 其内部结构是驱动程序定义的,通常用于:

  • 维护设备状态信息。

  • 为驱动程序使用的任何内核定义对象或其他系统资源(例如旋转锁)提供存储。

  • 保存驱动程序必须具有驻留和系统空间的任何数据才能执行其 I/O 操作。

由于大多数总线、函数和筛选器驱动程序 (最低级别驱动程序和中间驱动程序) 在任意线程上下文中执行, (因此无论线程发生当前) ,设备扩展都是每个驱动程序的主要位置,用于维护设备状态以及驱动程序所需的所有其他特定于设备的数据。 例如,任何实现 CustomTimerDpcCustomDpc 例程的驱动程序通常为设备扩展中所需的内核定义的计时器和/或 DPC 对象提供存储。

具有 ISR 的每个驱动程序都必须为指向一组内核定义的中断对象的指针提供存储,并且大多数设备驱动程序将此指针存储在设备扩展中。 每个驱动程序在创建设备对象时确定设备扩展的大小,每个驱动程序定义其自己的设备扩展的内容和结构。

I/O 管理器的 IoCreateDeviceIoCreateDeviceSecure 例程从非分页内存池为设备对象和扩展分配内存。

接收 IRP 的每个标准驱动程序例程也会接收指向表示所请求 I/O 操作的目标设备的设备对象的指针。 这些驱动程序例程可以通过此指针访问相应的设备扩展。 通常, DeviceObject 指针也是最低级别驱动程序 ISR 的输入参数。

下图显示了最低级别驱动程序设备对象的设备扩展的一组具有代表性的驱动程序定义数据。 较高级别的驱动程序不会为 IoConnectInterrupt 返回的中断对象指针提供存储,并且传递给 KeSynchronizeExecutionIoDisconnectInterrupt。 但是,如果驱动程序具有 CustomTimerDpc 例程,则较高级别的驱动程序将为下图中显示的计时器和 DPC 对象提供存储。 更高级别的驱动程序还可以为执行旋转锁和互锁工作队列提供存储。

diagram illustrating an example device extension for a lowest-level driver.

除了为中断对象指针提供存储外,如果中断旋转锁的 ISR 处理不同向量或具有多个 ISR 的两个或多个设备的中断,则最低级别的设备驱动程序还必须为中断旋转锁提供存储。 有关注册 ISR 的详细信息,请参阅 注册 ISR

通常,驱动程序在设备扩展中存储指向其设备对象的指针,如图所示。 驱动程序还可以保留扩展中设备的资源列表的副本。

较高级别的驱动程序通常在其设备扩展中存储指向下一个较低驱动程序的设备对象的指针。 在 IRP 中设置下一个较低驱动程序的 I/O 堆栈位置后,高级驱动程序必须将指向下一个较低驱动程序的设备对象的指针传递到 IoCallDriver,如 处理 IRP 中所述。

另请注意,为较低级别驱动程序分配 IRP 的任何较高级别驱动程序必须指定新 IRP 应具有多少个堆栈位置。 具体而言,如果较高级别的驱动程序调用 IoMakeAssociatedIrpIoAllocateIrpIoInitializeIrp,则必须访问下一级驱动程序的目标设备对象来读取其 StackSize 值,以便为这些支持例程提供正确的 StackSize 作为参数。

虽然较高级别的驱动程序可以通过 IoAttachDeviceToDeviceStack 返回的指针从下一级驱动程序的设备对象读取数据,但此类驱动程序必须遵循以下实现准则:

  • 从不尝试将数据写入较低驱动程序的设备对象。

    本指南的唯一例外是文件系统,这些文件系统在较低级别的可移动媒体驱动程序设备对象的 标志 中设置和清除DO_VERIFY_VOLUME。

  • 出于以下原因,从不尝试访问较低驱动程序的设备扩展:

    • 无法安全地在两个驱动程序之间同步对单个设备扩展的访问。

    • 无法单独升级实现此类后门通信方案的一对驱动程序,不能在它们之间插入中间驱动程序,而无需更改现有驱动程序源,并且无法重新编译并轻松地从一个Windows平台移动到下一个平台。

若要将其与较低级别的驱动程序的互操作性从一个Windows平台或版本保存到下一个版本,高级驱动程序必须重复使用给定的 IRP,或者必须创建新的 IRP,并且必须使用 IoCallDriver 将请求传达给较低级别的驱动程序。