2. WDM驱动对象与设备对象:DRIVER_OBJECT与DEVICE_OBJECT结构详解

在WDM框架中,驱动对象(DRIVER_OBJECT)与设备对象(DEVICE_OBJECT)是驱动开发者必须掌握的两个核心数据结构。它们构成了操作系统与驱动程序之间的通信桥梁,也是I/O请求分发、设备栈管理的基础。本章将深入剖析这两个结构体的内部布局、关键字段含义以及它们之间的关联关系。

2.1 DRIVER_OBJECT:驱动对象的基石

每个WDM驱动程序在加载时,I/O管理器会为其创建一个唯一的DRIVER_OBJECT实例。该结构体位于<wdm.h>中,是驱动与系统交互的入口点。其核心字段定义如下:

字段名 类型 说明
DriverSize CSHORT 驱动对象结构体的大小(字节数),用于版本兼容性检查。
DriverStart PVOID 驱动映像在内存中的起始地址。
DriverSection PVOID 指向驱动映像加载信息的内部指针(由系统管理)。
DriverExtension PDRIVER_EXTENSION 指向驱动扩展结构,包含驱动对象名称、服务键名等附加信息。
MajorFunction PDRIVER_DISPATCH[IRP_MJ_MAXIMUM_FUNCTION] 分发函数数组,每个IRP主功能码对应一个处理函数指针。这是驱动处理I/O请求的核心入口表。
DriverUnload PDRIVER_UNLOAD 驱动卸载回调函数指针,用于清理资源。
DeviceObject PDEVICE_OBJECT 指向该驱动创建的第一个设备对象的指针。通过此字段可遍历驱动管理的所有设备对象链表。
HardwareDatabase PVOID 指向硬件配置数据库的指针(通常用于PnP驱动)。
FastIoDispatch PFAST_IO_DISPATCH 快速I/O分发表指针(用于缓存文件系统等高性能场景)。
DriverInit PDRIVER_INITIALIZE 驱动初始化入口点(DriverEntry)的地址。
关键要点: MajorFunction 数组是驱动对象中最活跃的字段。驱动在 DriverEntry 中必须为至少一个IRP主功能码(如 IRP_MJ_CREATE、IRP_MJ_DEVICE_CONTROL)注册分发函数,否则系统无法与驱动正常通信。

2.2 DEVICE_OBJECT:设备对象的抽象

设备对象是WDM框架中表示“设备”的抽象结构。每个设备对象代表一个逻辑或物理设备,并参与设备栈的构建。其核心字段如下:

字段名 类型 说明
Type SHORT 对象类型,必须设置为 IO_TYPE_DEVICE。
Size USHORT 设备对象结构体的大小(字节数)。
ReferenceCount LONG 对象引用计数,系统用于管理对象生命周期。
DriverObject PDRIVER_OBJECT 指向创建该设备对象的驱动对象。这是设备对象与驱动对象之间的反向关联。
NextDevice PDEVICE_OBJECT 指向同一驱动创建的下一个设备对象,形成单链表。驱动可通过 DRIVER_OBJECT.DeviceObject 遍历此链表。
AttachedDevice PDEVICE_OBJECT 指向附加到该设备对象上层的设备对象(设备栈中的下一层)。
CurrentIrp PIRP 当前正在处理的IRP指针(主要用于Start I/O模式)。
Flags ULONG 设备对象特性标志,如 DO_BUFFERED_IO、DO_DIRECT_IO、DO_POWER_PAGABLE 等。
Characteristics ULONG 设备特性标志,如 FILE_DEVICE_UNKNOWN、FILE_REMOVABLE_MEDIA 等。
DeviceExtension PVOID 指向设备扩展结构的指针。驱动在此分配自定义数据(如硬件资源、状态标志、自旋锁等)。
DeviceType DEVICE_TYPE 设备类型,如 FILE_DEVICE_UNKNOWN、FILE_DEVICE_KEYBOARD 等。
StackSize CCHAR 设备栈中IRP堆栈单元的大小(字节数),用于IRP向下传递时分配堆栈空间。
AlignmentRequirement ULONG 设备对数据对齐的要求(通常为0或按字节对齐)。
开发提示: DeviceExtension 是驱动开发者最常使用的字段。在 IoCreateDevice 时指定扩展大小,系统会自动分配并清零。建议将设备相关的所有状态(如中断对象指针、Dpc对象、工作项、硬件寄存器基址等)集中存放在设备扩展中,避免使用全局变量。

2.3 驱动对象与设备对象的关联关系

驱动对象与设备对象之间存在双向关联,这种关系构成了WDM驱动管理设备的基础。下图展示了这种关联的静态结构:

DRIVER_OBJECT 与 DEVICE_OBJECT 关联结构图 DRIVER_OBJECT 与 DEVICE_OBJECT 关联结构 DRIVER_OBJECT DriverInit → DriverEntry 地址 DriverUnload → 卸载回调 MajorFunction[ ] → 分发函数表 DeviceObject → 指向第一个设备对象 DriverExtension → 扩展信息 FastIoDispatch → 快速I/O表 DeviceObject DEVICE_OBJECT (设备1) DriverObject → 指向所属驱动对象 NextDevice → 指向下一个设备对象 AttachedDevice → 指向附加的上层设备 DeviceExtension → 自定义扩展数据 Flags → DO_BUFFERED_IO 等 CurrentIrp → 当前IRP指针 DriverObject NextDevice 设备2 说明: 驱动对象通过 DeviceObject 字段指向其创建的第一个设备对象;每个设备对象通过 DriverObject 字段反向指向所属驱动对象。 同一驱动可创建多个设备对象,它们通过 NextDevice 字段形成单向链表。

2.4 设备对象链表遍历

驱动可以通过驱动对象的 DeviceObject 字段遍历其管理的所有设备对象。以下代码展示了典型的遍历模式:

// 遍历驱动对象下的所有设备对象
PDEVICE_OBJECT pDevObj = pDriverObject->DeviceObject;
while (pDevObj != NULL) {
    // 访问当前设备对象
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    
    // 处理设备对象...
    KdPrint(("设备对象: 0x%p, 扩展: 0x%p\n", pDevObj, pDevExt));
    
    // 移动到下一个设备对象
    pDevObj = pDevObj->NextDevice;
}
注意事项: 遍历设备对象链表时,必须持有适当的同步锁(如驱动对象的资源锁或设备扩展中的自旋锁),防止在遍历过程中设备对象被删除或卸载。在 DriverUnload 回调中遍历时,系统已保证不会有新的IRP到达,但仍需注意多核环境下的并发删除问题。

2.5 设备对象在设备栈中的位置

WDM框架中,设备对象通过 AttachedDevice 和 DeviceObject->StackSize 等字段参与设备栈的构建。下图展示了设备栈中设备对象的层次关系:

WDM设备栈中的设备对象层次 WDM设备栈中的设备对象层次 上层过滤驱动设备对象 Upper Filter DO AttachedDevice 功能驱动设备对象 Functional DO (FDO) AttachedDevice 下层过滤驱动设备对象 Lower Filter DO AttachedDevice 物理设备对象 Physical DO (PDO) IRP从设备栈顶层向下传递,每层设备对象通过 AttachedDevice 字段指向下一层。 驱动通过 IoAttachDeviceToDeviceStack 将自身设备对象附加到设备栈中。

2.6 设备扩展的设计模式

设备扩展(Device Extension)是驱动开发者自定义数据的主要载体。良好的设备扩展设计能显著提升代码的可维护性和可读性。以下是一个典型的设备扩展结构体定义:

// 设备扩展结构体定义
typedef struct _DEVICE_EXTENSION {
    // 设备对象指针(反向引用)
    PDEVICE_OBJECT  SelfDeviceObject;
    
    // 硬件资源
    PHYSICAL_ADDRESS    IoBaseAddress;      // I/O端口基址
    ULONG               IoRange;            // I/O端口范围
    PVOID               MappedRegisterBase; // 内存映射寄存器基址
    
    // 中断相关
    PKINTERRUPT         InterruptObject;    // 中断对象指针
    KSPIN_LOCK          SpinLock;           // 中断自旋锁
    BOOLEAN             InterruptRegistered; // 中断是否已注册
    
    // DPC对象
    KDPC                DpcForIsr;          // 用于ISR延后处理的DPC
    KTIMER              TimerForDpc;        // 定时器对象(可选)
    
    // 工作项
    IO_WORKITEM         *pWorkItem;         // 系统工作项指针
    
    // 设备状态
    ULONG               DeviceState;        // 设备状态标志
    BOOLEAN             DeviceRemoved;      // 设备是否已移除
    
    // 同步对象
    FAST_MUTEX          Mutex;              // 快速互斥体
    
    // IOCTL相关
    ULONG               IoctlBufferSize;    // IOCTL缓冲区大小
    PVOID               IoctlBuffer;        // IOCTL缓冲区指针
    
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
设计建议:
  • 设备扩展中应包含 SelfDeviceObject 字段,方便在扩展中反向获取设备对象指针。
  • 所有需要跨IRQL访问的数据(如中断处理中使用的标志)必须使用自旋锁保护。
  • 避免在设备扩展中放置大块缓冲区,建议使用动态分配或Lookaside列表管理。
  • 设备扩展在 IoCreateDevice 时分配,在 IoDeleteDevice 时自动释放,无需手动管理内存。

2.7 驱动对象与设备对象的生命周期

理解驱动对象与设备对象的创建和销毁时机,对于正确管理资源至关重要。下表总结了它们的生命周期关键点:

阶段 驱动对象 设备对象
创建 由I/O管理器在驱动加载时自动创建 由驱动在 DriverEntry 或 AddDevice 中调用 IoCreateDevice 创建
初始化 在 DriverEntry 中填充分发函数指针、卸载回调等 在 AddDevice 中设置设备扩展、注册中断、附加设备栈等
运行期 作为分发函数调用的上下文,通过 DriverObject 参数传入 作为IRP处理的目标,通过 DeviceObject 参数传入
销毁 由I/O管理器在驱动卸载时自动销毁 由驱动在 DriverUnload 或 RemoveDevice 中调用 IoDeleteDevice 销毁
重要规则: 设备对象必须在驱动卸载前全部删除。如果驱动卸载时仍有未删除的设备对象,系统会触发蓝屏(BugCheck 0xCE: DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS)。建议在 DriverUnload 中遍历设备对象链表并逐一删除。

2.8 本章小结

  • DRIVER_OBJECT 是驱动与系统交互的入口,核心字段包括 MajorFunction 分发函数数组、DeviceObject 设备对象链表头、DriverUnload 卸载回调。
  • DEVICE_OBJECT 是设备抽象的核心,关键字段包括 DriverObject 反向引用、NextDevice 链表指针、AttachedDevice 设备栈链接、DeviceExtension 自定义数据区。
  • 驱动对象与设备对象通过 DeviceObject 和 DriverObject 字段形成双向关联,支持设备对象链表的遍历。
  • 设备对象通过 AttachedDevice 字段参与设备栈构建,IRP在设备栈中逐层传递。
  • 设备扩展是驱动开发者管理设备状态的主要手段,应包含所有设备相关的自定义数据,并注意同步保护。