设备扩展Device Extensions

对于大多数中间和最低级别的驱动程序,设备扩展是与设备对象相关的最重要的数据结构。For most intermediate and lowest-level drivers, the device extension is the most important data structure associated with a device object. 它的内部结构是由驱动程序定义的,它通常用于:Its internal structure is driver-defined, and it is typically used to:

  • 维护设备状态信息。Maintain device state information.

  • 为驱动程序使用的任何内核定义的对象或其他系统资源(如旋转锁)提供存储。Provide storage for any kernel-defined objects or other system resources, such as spin locks, used by the driver.

  • 保存驱动程序必须驻留的任何数据,并保存在系统空间中以执行其 i/o 操作。Hold any data the driver must have resident and in system space to carry out its I/O operations.

由于大多数总线、函数和筛选器驱动程序都 (最低级别和中间驱动程序) 在任意线程上下文中执行, (任何线程发生在当前) 的情况下,设备扩展就是用于维护设备状态的每个驱动程序的主要位置以及驱动程序所需的所有其他设备特定数据。Because most bus, function, and filter drivers (lowest-level and intermediate drivers) execute in an arbitrary thread context (that of whatever thread happens to be current), a device extension is each driver's primary place to maintain device state and all other device-specific data the driver needs. 例如,任何实现 CustomTimerDpcCustomDpc 例程的驱动程序通常都会为设备扩展中所需的内核定义的计时器和/或 DPC 对象提供存储空间。For example, any driver that implements a CustomTimerDpc or CustomDpc routine usually provides storage for the required kernel-defined timer and/or DPC objects in a device extension.

具有 ISR 的每个驱动程序都必须为指向一组内核定义中断对象的指针提供存储,并且大多数设备驱动程序将此指针存储在设备扩展中。Every driver that has an ISR must provide storage for a pointer to a set of kernel-defined interrupt objects, and most device drivers store this pointer in a device extension. 每个驱动程序在创建设备对象时确定设备扩展的大小,每个驱动程序定义其自己的设备扩展的内容和结构。Each driver determines the size of the device extension when it creates a device object, and each driver defines the contents and structure of its own device extensions.

I/o 管理器的 IoCreateDeviceIoCreateDeviceSecure 例程为设备对象分配内存,并为非分页内存池分配扩展。The I/O manager's IoCreateDevice and IoCreateDeviceSecure routines allocate memory for the device object and extension from the nonpaged memory pool.

接收 IRP 的每个标准驱动程序例程还会接收指向设备对象的指针,该对象表示请求的 i/o 操作的目标设备。Every standard driver routine that receives an IRP also receives a pointer to a device object representing the target device for the requested I/O operation. 这些驱动程序例程可以通过此指针访问相应的设备扩展。These driver routines can access the corresponding device extension through this pointer. 通常, DeviceObject 指针也是最底层驱动程序 ISR 的输入参数。Usually, a DeviceObject pointer is also an input parameter to a lowest-level driver's ISR.

下图显示了一组代表驱动程序定义的数据,适用于最底层驱动程序的设备对象的设备扩展。The following figure shows a representative set of driver-defined data for the device extension of a lowest-level driver's device object. 较高级别的驱动程序不会为 IoConnectInterrupt 返回的中断对象指针提供存储,并将其传递给 KeSynchronizeExecutionIoDisconnectInterruptA higher-level driver would not provide storage for an interrupt object pointer returned by IoConnectInterrupt and passed to KeSynchronizeExecution and IoDisconnectInterrupt. 但是,如果驱动程序具有 CustomTimerDpc 例程,则更高级别的驱动程序将为下图中所示的计时器和 DPC 对象提供存储空间。However, a higher-level driver would provide storage for the timer and DPC objects shown in the following figure if the driver has a CustomTimerDpc routine. 较高级别的驱动程序还可以为执行人员旋转锁定和联锁工作队列提供存储。A higher-level driver also might provide storage for an executive spin lock and interlocked work queue.

说明最低级别驱动程序的设备扩展示例的关系图

除了为中断对象指针提供存储外,如果其 ISR 处理不同矢量上两个或更多设备的中断或者具有多个 ISR,则最低级别的设备驱动程序必须为中断旋转锁定提供存储。In addition to providing storage for an interrupt object pointer, a lowest-level device driver must supply storage for an interrupt spin lock if its ISR handles interrupts for two or more devices on different vectors or if it has more than one ISR. 有关注册 ISR 的详细信息,请参阅 注册 isrFor more information about registering an ISR, see Registering an ISR.

通常,驱动程序会在其设备扩展中存储指向其设备对象的指针,如图所示。Typically, drivers store pointers to their device objects in their device extensions, as shown in the figure. 驱动程序还可以保留设备在扩展中的资源列表的副本。A driver might also keep a copy of the resource list for the device in the extension.

较高级别的驱动程序通常会在其设备扩展中存储一个指向下一个较低驱动程序的设备对象的指针。A higher-level driver typically stores a pointer to the next-lower driver's device object in its device extension. 更高级别的驱动程序必须将指向下一个较低驱动程序的设备对象的指针传递到 IoCallDriver,然后再在 irp 中设置下一个较低版本的驱动程序的 i/o 堆栈位置(如 处理 irp中所述)。A higher-level driver must pass a pointer to the next-lower driver's device object to IoCallDriver, after it has set up the next-lower driver's I/O stack location in an IRP, as explained in Handling IRPs.

另请注意,为较低级别的驱动程序分配 Irp 的任何较高级别的驱动程序必须指定新的 Irp 应具有多少个堆栈位置。Note also that any higher-level driver that allocates IRPs for lower-level drivers must specify how many stack locations the new IRPs should have. 特别是,如果较高级别的驱动程序调用 IoMakeAssociatedIrpIoAllocateIrpIoInitializeIrp,则它必须访问下一级驱动程序的目标设备对象,以读取其 StackSize 值,以便提供正确的 StackSize 作为这些支持例程的参数。In particular, if a higher-level driver calls IoMakeAssociatedIrp, IoAllocateIrp, or IoInitializeIrp, it must access the target device object of the next-lower-level driver to read its StackSize value, in order to supply the correct StackSize as an argument to these support routines.

虽然更高级别的驱动程序可以通过 IoAttachDeviceToDeviceStack返回的指针从下一级驱动程序的设备对象中读取数据,但此类驱动程序必须遵循以下实现准则:While a higher-level driver can read data from the next-lower-level driver's device object through the pointer returned by IoAttachDeviceToDeviceStack, such a driver must follow these implementation guidelines:

  • 切勿尝试将数据写入驱动程序的设备对象。Never attempt to write data to the lower driver's device object.

    此准则的唯一例外是 "文件系统",这些系统设置和 _ 清除 _ "在较低级别的可移动媒体驱动程序的设备对象 标志 中验证卷"。The only exceptions to this guideline are file systems, which set and clear DO_VERIFY_VOLUME in the Flags of lower-level removable-media drivers' device objects.

  • 由于以下原因,永远不要尝试访问该驱动程序的设备扩展:Never attempt to access the lower driver's device extension for the following reasons:

    • 没有安全的方法可以同步对两个驱动程序之间的单个设备扩展的访问。There is no safe way to synchronize access to a single device extension between two drivers.
    • 一对实现此类后门通信方案的驱动程序不能单独升级,不能在不更改现有驱动程序源的情况下在它们之间插入中间驱动程序,并且不能轻松地从一个 Windows 平台重新编译并将其移动到下一平台。A pair of drivers that implement such a backdoor communication scheme cannot be upgraded individually, cannot have an intermediate driver inserted between them without changing existing driver source, and cannot be recompiled and moved readily from one Windows platform to the next.

若要保持与较低级别驱动程序的互操作性(从一个 Windows 平台或版本到下一个版本),更高级别的驱动程序必须重新使用其给定的 Irp,或者必须创建新的 Irp,并且它们必须使用 IoCallDriver 将请求传递给较低级别的驱动程序。To preserve their interoperability with lower-level drivers from one Windows platform or version to the next, higher-level drivers either must reuse the IRPs given them or must create new IRPs, and they must use IoCallDriver to communicate requests to lower-level drivers.