用有限的几句话就舒舒服服的建立起对驱动对象和设备对象的概念是不可能的。刚开始是一片模糊,了解的多了,慢慢就清楚。下面的内容会使你对他们了解的清楚些。
为了后面的叙述方便,首先列出驱动对象和设备对象的结构。
驱动对象结构 DRIVER_OBJECT ,定义如下
struct _DRIVER_OBJECT (sizeof=168)
+00 int16 Type
+02 int16 Size
+04 struct _DEVICE_OBJECT *DeviceObject
+08 uint32 Flags
+0c void *DriverStart
+10 uint32 DriverSize
+14 void *DriverSection
+18 struct _DRIVER_EXTENSION *DriverExtension
+1c struct _UNICODE_STRING DriverName
+1c uint16 Length
+1e uint16 MaximumLength
+20 uint16 *Buffer
+24 struct _UNICODE_STRING *HardwareDatabase
+28 struct _FAST_IO_DISPATCH *FastIoDispatch
+2c function *DriverInit
+30 function *DriverStartIo
+34 function *DriverUnload
+38 function *MajorFunction[28]
DDK 中有对于一些域的说明
[00] IRP_MJ_CREATE
[01] IRP_MJ_CREATE_NAMED_PIPE
[02] IRP_MJ_CLOSE
[03] IRP_MJ_READ
[04] IRP_MJ_WRITE
[05] IRP_MJ_QUERY_INFORMATION
[06] IRP_MJ_SET_INFORMATION
[07] IRP_MJ_QUERY_EA
[08] IRP_MJ_SET_EA
[09] IRP_MJ_FLUSH_BUFFERS
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION
[0b] IRP_MJ_SET_VOLUME_INFORMATION
[0c] IRP_MJ_DIRECTORY_CONTROL
[0d] IRP_MJ_FILE_SYSTEM_CONTROL
[0e] IRP_MJ_DEVICE_CONTROL
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL
[10] IRP_MJ_SHUTDOWN
[11] IRP_MJ_LOCK_CONTROL
[12] IRP_MJ_CLEANUP
[13] IRP_MJ_CREATE_MAILSLOT
[14] IRP_MJ_QUERY_SECURITY
[15] IRP_MJ_SET_SECURITY
[16] IRP_MJ_POWER
[17] IRP_MJ_SYSTEM_CONTROL
[18] IRP_MJ_DEVICE_CHANGE
[19] IRP_MJ_QUERY_QUOTA
[1a] IRP_MJ_SET_QUOTA
[1b] IRP_MJ_PNP
struct _DRIVER_EXTENSION (sizeof=24)
+00 struct _DRIVER_OBJECT *DriverObject
+04 function *AddDevice
+08 uint32 Count
+0c struct _UNICODE_STRING ServiceKeyName
+0c uint16 Length
+0e uint16 MaximumLength
+10 uint16 *Buffer
+14 struct _IO_CLIENT_EXTENSION *ClientDriverExtension
设备对象结构 DEVICE_OBJECT ,定义如下
struct _DEVICE_OBJECT (sizeof=184)
+00 int16 Type
+02 uint16 Size
+04 int32 ReferenceCount
+08 struct _DRIVER_OBJECT *DriverObject
+0c struct _DEVICE_OBJECT *NextDevice
+10 struct _DEVICE_OBJECT *AttachedDevice
+14 struct _IRP *CurrentIrp
+18 struct _IO_TIMER *Timer
+1c uint32 Flags
+20 uint32 Characteristics
+24 struct _VPB *Vpb
+28 void *DeviceExtension
+2c uint32 DeviceType
+30 char StackSize
+34 union __unnamed62 Queue
+34 struct _LIST_ENTRY ListEntry
+34 struct _LIST_ENTRY *Flink
+38 struct _LIST_ENTRY *Blink
+34 struct _WAIT_CONTEXT_BLOCK Wcb
+34 struct _KDEVICE_QUEUE_ENTRY WaitQueueEntry
+34 struct _LIST_ENTRY DeviceListEntry
+34 struct _LIST_ENTRY *Flink
+38 struct _LIST_ENTRY *Blink
+3c uint32 SortKey
+40 byte Inserted
+44 function *DeviceRoutine
+48 void *DeviceContext
+4c uint32 NumberOfMapRegisters
+50 void *DeviceObject
+54 void *CurrentIrp
+58 struct _KDPC *BufferChainingDpc
+5c uint32 AlignmentRequirement
+60 struct _KDEVICE_QUEUE DeviceQueue
+60 int16 Type
+62 int16 Size
+64 struct _LIST_ENTRY DeviceListHead
+64 struct _LIST_ENTRY *Flink
+68 struct _LIST_ENTRY *Blink
+6c uint32 Lock
+70 byte Busy
+74 struct _KDPC Dpc
+74 int16 Type
+76 byte Number
+77 byte Importance
+78 struct _LIST_ENTRY DpcListEntry
+78 struct _LIST_ENTRY *Flink
+7c struct _LIST_ENTRY *Blink
+80 function *DeferredRoutine
+84 void *DeferredContext
+88 void *SystemArgument1
+8c void *SystemArgument2
+90 uint32 *Lock
+94 uint32 ActiveThreadCount
+98 void *SecurityDescriptor
+9c struct _KEVENT DeviceLock
+9c struct _DISPATCHER_HEADER Header
+9c byte Type
+9d byte Absolute
+9e byte Size
+9f byte Inserted
+a0 int32 SignalState
+a4 struct _LIST_ENTRY WaitListHead
+a4 struct _LIST_ENTRY *Flink
+a8 struct _LIST_ENTRY *Blink
+ac uint16 SectorSize
+ae uint16 Spare1
+b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension
+b4 void *Reserved
DDK 中有对于一些域的说明
struct _DEVOBJ_EXTENSION (sizeof=36)
+00 int16 Type
+02 uint16 Size
+04 struct _DEVICE_OBJECT *DeviceObject
+08 uint32 PowerFlags
+0c *Dope
+10 uint32 ExtensionFlags
+14 void *DeviceNode
+18 struct _DEVICE_OBJECT *AttachedTo
+1c struct _LIST_ENTRY FileObjectList
+1c struct _LIST_ENTRY *Flink
+20 struct _LIST_ENTRY *Blink
有一个载入内存的驱动程序文件,就会有一个驱动对象(DRIVER_OBJECT)。
比如,驱动 i8042prt 是在系统初始化阶段,IO 管理器调用函数 nt!IopLoadDriver 载入的。nt!IopLoadDriver 读取注册表,获得驱动程序文件 i8042prt.SYS 的路径,将这个文件载入内存。之后会调用 nt!ObCreateObject 创建一个驱动对象,并初始化这个驱动对象。
设备对象(DEVICE_OBJECT)由驱动创建。一个驱动可以创建多个设备对象(DEVICE_OBJECT)。通过驱动对象(DRIVER_OBJECT),可以找到由该驱动创建的所有设备对象(DEVICE_OBJECT)。一个驱动创建的所有设备对象(DEVICE_OBJECT)链成一条链,该驱动的驱动对象(DRIVER_OBJECT)可以找到这个链。一个设备对象(DEVICE_OBJECT)也可以找到创建它的驱动的驱动对象(DRIVER_OBJECT)。下面我们使用 WinDbg 实际观察驱动 i8042prt 的情况,i8042prt 是完成 ps/2 键盘驱动主要功能的驱动程序。
在这里我们先做个说明,对于同时插有 ps/2 键盘和 ps/2 鼠标的计算机上, i8042prt 会创建两个设备对象,一个用于键盘,一个用于鼠标。
首先我们从命名对象地址空间,找到 i8042prt 的 DRIVER_OBJECT 的地址
kd> !object \driver\i8042prt
Object: fe4f69f0 Type: (fe4ec3e0) Driver
ObjectHeader: fe4f69d8
HandleCount: 0 PointerCount: 5
Directory Object: fe51bf30 Name: i8042prt
下面我们看 i8042prt 的 DRIVER_OBJECT 中的内容
kd> !strct driver_object fe4f69f0
struct _DRIVER_OBJECT (sizeof=168)
+00 int16 Type = 0004
+02 int16 Size = 00a8
+04 struct _DEVICE_OBJECT *DeviceObject = FE4D3BA0
// 注意 DeviceObject 链上的第一个设备对象
+08 uint32 Flags = 00000012
+0c void *DriverStart = FE1BD000
+10 uint32 DriverSize = 00021000
+14 void *DriverSection = FE4F6CC8
+18 struct _DRIVER_EXTENSION *DriverExtension = FE4F6A98
+1c struct _UNICODE_STRING DriverName
+1c uint16 Length = 0020
+1e uint16 MaximumLength = 0020
+20 uint16 *Buffer = E127FE88
+24 struct _UNICODE_STRING *HardwareDatabase = 80541FC0
+28 struct _FAST_IO_DISPATCH *FastIoDispatch = 00000000
+2c function *DriverInit = FE1D8000
+30 function *DriverStartIo = FE1BF444
+34 function *DriverUnload = FE1CB920
+38 function *MajorFunction[28] = FE1CB000
80425354
FE1CB14E
80425354
80425354
80425354
80425354
80425354
80425354
FE1BE558
80425354
80425354
80425354
80425354
FE1CB243
FE1BE667
80425354
80425354
80425354
80425354
80425354
80425354
FE1D2C63
FE1D3633
80425354
80425354
80425354
FE1D1BFB
我们看到了 DeviceObject 上的第一个设备对象的地址 FE4D3BA0 ,我们使用 WinDbg 的 !devobj 命令,来获得一些信息
kd> !devobj FE4D3BA0
Device object (fe4d3ba0) is for:
\Driver\i8042prt DriverObject fe4f69f0
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt fe4d3c58 DevObjExt fe4d3ed8
ExtensionFlags (0xc0000000)
Unknown flags 0xc0000000
AttachedDevice (Upper) fe4d3a60 \Driver\vmmouse
AttachedTo (Lower) fe4dd610 \Driver\ACPI
Device queue is not busy.
从获得的信息来看,这是用于鼠标的那个设备对象,下面我们看这个设备对象 DEVICE_OBJECT 中的具体内容
kd> !strct device_object FE4D3BA0
struct _DEVICE_OBJECT (sizeof=184)
+00 int16 Type = 0003
+02 uint16 Size = 0338
+04 int32 ReferenceCount = 00000000
+08 struct _DRIVER_OBJECT *DriverObject = FE4F69F0
// 正是 \Driver\i8042prt 的驱动对象的地址
+0c struct _DEVICE_OBJECT *NextDevice = FE4F5020
// DeviceObject 链的下一个
+10 struct _DEVICE_OBJECT *AttachedDevice = FE4D3A60
+14 struct _IRP *CurrentIrp = 00000000
...
我们看到了设备对象 +0c struct _DEVICE_OBJECT *NextDevice 处是 DeviceObject 链上的下一个设备对象的地址,值为 FE4F5020 ,我们使用 WinDbg 的 !devobj 命令,来获得一些信息
kd> !devobj FE4F5020
Device object (fe4f5020) is for:
\Driver\i8042prt DriverObject fe4f69f0
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt fe4f50d8 DevObjExt fe4f5358
ExtensionFlags (0xc0000000)
Unknown flags 0xc0000000
AttachedDevice (Upper) fe4f5df0 \Driver\Kbdclass
AttachedTo (Lower) fe4dd730 \Driver\ACPI
Device queue is not busy.
从获得的信息来看,这是用于键盘的那个设备对象,下面我们看这个设备对象 DEVICE_OBJECT 中的具体内容
kd> !strct device_object FE4F5020
struct _DEVICE_OBJECT (sizeof=184)
+00 int16 Type = 0003
+02 uint16 Size = 0338
+04 int32 ReferenceCount = 00000000
+08 struct _DRIVER_OBJECT *DriverObject = FE4F69F0
// 正是 \Driver\i8042prt 的驱动对象的地址
+0c struct _DEVICE_OBJECT *NextDevice = 00000000
// 为空,表明链结束了。
+10 struct _DEVICE_OBJECT *AttachedDevice = FE4F5DF0
+14 struct _IRP *CurrentIrp = 00000000
...
我们观察初始化时,看到 i8042prt 调用 IoCreateDevice 创建设备对象,并且 IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中。
现在我们对驱动对象和设备对象之间的联系,做一个总结,
驱动调用 IoCreateDevice 创建设备对象,一个驱动创建的所有设备对象链成一个链,这个链以空表示结束。驱动对象通过 +04 struct _DEVICE_OBJECT *DeviceObject 可以找到这个链。设备对象通过 +0c struct _DEVICE_OBJECT *NextDevice 链在一起。设备对象通过 +08 struct _DRIVER_OBJECT *DriverObject 可以找到创建它的驱动的驱动对象。
下面我们来讲讲设备栈。一个物理设备的驱动任务,通常由几个驱动程序一层一层的共同完成。每层一个设备对象,他们联系在一起,组成一个设备栈。一个设备栈上的设备对象,从上自下,联系在一起。从下自上,也联系在一起。我们以 ps/2 键盘驱动为例,使用 WinDbg 来观察它的设备栈,以及他们如何联系在一起。
ps/2 键盘驱动的设备栈,大概就是这个样子
kd> !devstack fe4f5020
!DevObj !DrvObj !DevExt ObjectName
fe4f5df0 \Driver\Kbdclass fe4f5ea8 KeyboardClass0
> fe4f5020 \Driver\i8042prt fe4f50d8
fe4dd730 \Driver\ACPI fe507468 0000000e
!DevNode fe4fed68 :
DeviceInst is "ACPI\PNP0303\4&5289e18&0"
ServiceName is "i8042prt"
最上面的 DEVICE_OBJECT 地址为 fe4f5df0,属于驱动 \Driver\Kbdclass
中间的 DEVICE_OBJECT 地址为 fe4f5020,属于驱动 \Driver\i8042prt
最下面的 DEVICE_OBJECT 地址为 fe4dd730,属于驱动 \Driver\ACPI
下面我们看这3个设备对象,如何联系在一起。
最上面一层的 DEVICE_OBJECT
kd> !devobj fe4f5df0
Device object (fe4f5df0) is for:
KeyboardClass0 \Driver\Kbdclass DriverObject fe4f6330
Current Irp fe43a1c8 RefCount 0 Type 0000000b Flags 00002044
DevExt fe4f5ea8 DevObjExt fe4f5fd8
ExtensionFlags (0000000000)
AttachedTo (Lower) fe4f5020 \Driver\i8042prt
Device queue is busy -- Queue empty.
看到这个设备对象有名字,叫 KeyboardClass0 ,属于驱动 Kbdclass
kd> !strct device_object fe4f5df0
struct _DEVICE_OBJECT (sizeof=184)
+00 int16 Type = 0003
+02 uint16 Size = 01e8
+04 int32 ReferenceCount = 00000000
+08 struct _DRIVER_OBJECT *DriverObject = FE4F6330
// 正是 kbdclass 的 DRIVER_OBJECT 的地址
+0c struct _DEVICE_OBJECT *NextDevice = 00000000
+10 struct _DEVICE_OBJECT *AttachedDevice = 00000000
// 为空结束,表示上面没有其它设备对象了
+14 struct _IRP *CurrentIrp = FE43A1C8
...
+ae uint16 Spare1 = 0000
+b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension = FE4F5FD8
// 它的 DEVOBJ_EXTENSION 在 FE4F5FD8
+b4 void *Reserved = 00000000
kd> !strct DEVOBJ_EXTENSION FE4F5FD8
struct _DEVOBJ_EXTENSION (sizeof=36)
+00 int16 Type = 000d
+02 uint16 Size = 0000
+04 struct _DEVICE_OBJECT *DeviceObject = FE4F5DF0
+08 uint32 PowerFlags = 00000010
+0c *Dope = 00000000
+10 uint32 ExtensionFlags = 00000000
+14 void *DeviceNode = 00000000
+18 struct _DEVICE_OBJECT *AttachedTo = FE4F5020
// 下面一层的设备对象为 FE4F5020
+1c struct _LIST_ENTRY FileObjectList
+1c struct _LIST_ENTRY *Flink = 00000000
+20 struct _LIST_ENTRY *Blink = 00000000
中间层的 DEVICE_OBJECT
kd> !devobj fe4f5020
Device object (fe4f5020) is for:
\Driver\i8042prt DriverObject fe4f69f0
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt fe4f50d8 DevObjExt fe4f5358
ExtensionFlags (0xc0000000)
Unknown flags 0xc0000000
AttachedDevice (Upper) fe4f5df0 \Driver\Kbdclass
AttachedTo (Lower) fe4dd730 \Driver\ACPI
Device queue is not busy.
看到这个设备对象没有名字,属于驱动 i8042prt
kd> !strct device_object fe4f5020
struct _DEVICE_OBJECT (sizeof=184)
+00 int16 Type = 0003
+02 uint16 Size = 0338
+04 int32 ReferenceCount = 00000000
+08 struct _DRIVER_OBJECT *DriverObject = FE4F69F0
// 正是 i8042prt 的 DRIVER_OBJECT 的地址
+0c struct _DEVICE_OBJECT *NextDevice = 00000000
+10 struct _DEVICE_OBJECT *AttachedDevice = FE4F5DF0
// 上面一层的设备对象
+14 struct _IRP *CurrentIrp = 00000000
...
+ae uint16 Spare1 = 0001
+b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension = FE4F5358
// 它的 DEVOBJ_EXTENSION 在 FE4F5358
+b4 void *Reserved = 00000000
kd> !strct DEVOBJ_EXTENSION FE4F5358
struct _DEVOBJ_EXTENSION (sizeof=36)
+00 int16 Type = 000d
+02 uint16 Size = 0000
+04 struct _DEVICE_OBJECT *DeviceObject = FE4F5020
+08 uint32 PowerFlags = 00000000
+0c *Dope = 00000000
+10 uint32 ExtensionFlags = c0000000
+14 void *DeviceNode = 00000000
+18 struct _DEVICE_OBJECT *AttachedTo = FE4DD730
// 下面一层的设备对象
+1c struct _LIST_ENTRY FileObjectList
+1c struct _LIST_ENTRY *Flink = 00000000
+20 struct _LIST_ENTRY *Blink = 00000000
最下面一层的 DEVICE_OBJECT
kd> !devobj fe4dd730
Device object (fe4dd730) is for:
0000000e \Driver\ACPI DriverObject fe4e8ad0
Current Irp 00000000 RefCount 1 Type 00000032 Flags 00001040
DevExt fe507468 DevObjExt fe4dd7e8 DevNode fe4fed68
ExtensionFlags (0000000000)
AttachedDevice (Upper) fe4f5020 \Driver\i8042prt
Device queue is not busy.
看到这个设备对象有名字,叫 0000000e ,属于驱动 acpi
kd> !strct device_object fe4dd730
struct _DEVICE_OBJECT (sizeof=184)
+00 int16 Type = 0003
+02 uint16 Size = 00b8
+04 int32 ReferenceCount = 00000001
+08 struct _DRIVER_OBJECT *DriverObject = FE4E8AD0
// 正是 acpi 的 DRIVER_OBJECT 的地址
+0c struct _DEVICE_OBJECT *NextDevice = FE4DD850
+10 struct _DEVICE_OBJECT *AttachedDevice = FE4F5020
// 上面一层的设备对象
+14 struct _IRP *CurrentIrp = 00000000
...
+ae uint16 Spare1 = 0001
+b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension = FE4DD7E8
// 它的 DEVOBJ_EXTENSION 在 FE4DD7E8
+b4 void *Reserved = 00000000
kd> !strct DEVOBJ_EXTENSION FE4DD7E8
struct _DEVOBJ_EXTENSION (sizeof=36)
+00 int16 Type = 000d
+02 uint16 Size = 0000
+04 struct _DEVICE_OBJECT *DeviceObject = FE4DD730
+08 uint32 PowerFlags = 00000010
+0c *Dope = 00000000
+10 uint32 ExtensionFlags = 00000000
+14 void *DeviceNode = FE4FED68
+18 struct _DEVICE_OBJECT *AttachedTo = 00000000
// 为空结束,表示下面没有其它设备对象了
+1c struct _LIST_ENTRY FileObjectList
+1c struct _LIST_ENTRY *Flink = 00000000
+20 struct _LIST_ENTRY *Blink = 00000000
从上自下。\Device\KeyboardClass0(DRIVER_OBJECT 是 kbdclass)可以找到 没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)。没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)可以找到 \Device\0000000e(DRIVER_OBJECT 是 ACPI)。
从下自上。\Device\0000000e(DRIVER_OBJECT 是 ACPI) 可以找到 没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)。没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)可以找到 \Device\KeyboardClass0(DRIVER_OBJECT 是 ACPI)。
现在我们对设备栈上的设备对象之间的联系,做一个总结,
设备栈上的设备对象,从上自下,通过 DEVICE_OBJECT 的 DEVOBJ_EXTENSION 的 +18 struct _DEVICE_OBJECT *AttachedTo 联系在一起。
设备栈上的设备对象,从下自上,通过 DEVICE_OBJECT 的 +10 struct _DEVICE_OBJECT *AttachedDevice 联系在一起。
在观察键盘驱动的初始化过程中,我们看到了设备栈是如何建立起来的。
驱动 i8042prt ,驱动 kbdclass 分别调用 IoAttachDeviceToDeviceStack ,把自己相应的设备对象加入到键盘设备栈中。IoAttachDeviceToDeviceStack 会设置 DEVICE_OBJECT 的 DEVOBJ_EXTENSION 的 +18 struct _DEVICE_OBJECT *AttachedTo 和 DEVICE_OBJECT 的 +10 struct _DEVICE_OBJECT *AttachedDevice ,把两个对象联系在一起。并设置新加入的设备对象的 +30 char StackSize
![](https://img2024.cnblogs.com/blog/35695/202408/35695-20240826070647288-243310440.jpg)