1. Windows驱动开发概述
Windows驱动开发是操作系统底层开发中最具挑战性的领域之一。作为连接硬件设备与操作系统的桥梁,驱动程序直接运行在内核模式,拥有对系统资源的最高访问权限。这种特权地位使得驱动程序的稳定性和安全性对整个系统的健康运行至关重要。
在Windows驱动开发中,数据结构扮演着核心角色。与用户态程序不同,内核态驱动需要处理大量由操作系统定义的特殊数据结构,这些结构体构成了驱动与内核交互的"语言"。理解这些数据结构不仅关系到驱动功能的实现,更直接影响系统的稳定性和性能。
警告:内核模式编程的一个微小错误就可能导致系统蓝屏崩溃。在开始驱动开发前,务必确保具备扎实的C语言基础和操作系统原理知识。
2. 驱动核心数据结构解析
2.1 设备对象(DEVICE_OBJECT)
设备对象是Windows驱动模型中最基础的数据结构之一,它代表了一个物理或逻辑设备在内核中的抽象。每个驱动至少需要创建一个设备对象,作为I/O请求的处理端点。
c复制typedef struct _DEVICE_OBJECT {
CSHORT Type;
USHORT Size;
LONG ReferenceCount;
struct _DRIVER_OBJECT *DriverObject;
struct _DEVICE_OBJECT *NextDevice;
struct _DEVICE_OBJECT *AttachedDevice;
// ...其他成员省略
} DEVICE_OBJECT;
关键成员解析:
DriverObject:指向创建该设备对象的驱动对象NextDevice:指向同一个驱动创建的下一个设备对象,形成链表AttachedDevice:在设备栈中,指向附加到当前设备之上的设备对象
设备对象的创建通常发生在驱动的DriverEntry例程中,使用IoCreateDevice API。值得注意的是,设备对象一旦创建就会一直存在,直到驱动被卸载。
2.2 驱动对象(DRIVER_OBJECT)
驱动对象代表了一个加载到内存中的驱动程序实例。它包含了驱动的主要入口点和一些关键信息。
c复制typedef struct _DRIVER_OBJECT {
CSHORT Type;
CSHORT Size;
PDEVICE_OBJECT DeviceObject;
ULONG Flags;
PVOID DriverStart;
ULONG DriverSize;
PVOID DriverSection;
PDRIVER_EXTENSION DriverExtension;
UNICODE_STRING DriverName;
// ...其他成员省略
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;
其中最重要的成员是MajorFunction数组,它包含了驱动对各种I/O请求包(IRP)的处理函数指针。驱动开发者需要根据需求实现特定的处理例程,如:
IRP_MJ_CREATE:处理打开设备请求IRP_MJ_READ:处理读请求IRP_MJ_WRITE:处理写请求IRP_MJ_DEVICE_CONTROL:处理设备控制请求
2.3 I/O请求包(IRP)
IRP是Windows I/O系统的核心数据结构,它封装了所有的I/O操作请求。当应用程序发起I/O操作时,I/O管理器会创建一个IRP并发送给相应的驱动栈。
c复制typedef struct _IRP {
PMDL MdlAddress;
ULONG Flags;
union {
struct _IRP *MasterIrp;
// ...其他联合成员
} AssociatedIrp;
IO_STATUS_BLOCK IoStatus;
KPROCESSOR_MODE RequestorMode;
BOOLEAN PendingReturned;
CHAR StackCount;
CHAR CurrentLocation;
// ...其他成员省略
} IRP;
IRP的关键特性包括:
- 栈式处理:IRP可以包含多个栈位置(stack location),每个驱动在处理时可以访问自己的栈位置
- 异步支持:IRP可以异步完成,不会阻塞调用线程
- 内存管理:通过MDL(Memory Descriptor List)描述用户缓冲区
3. 驱动扩展数据结构
3.1 内存描述符列表(MDL)
MDL用于描述物理内存页面与虚拟地址的映射关系,是驱动与用户空间交换数据的重要机制。
c复制typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
驱动开发者通常使用以下API操作MDL:
IoAllocateMdl:分配MDLMmBuildMdlForNonPagedPool:为非分页池构建MDLMmProbeAndLockPages:探测并锁定用户模式内存MmMapLockedPagesSpecifyCache:映射锁定的页面
经验分享:在处理用户模式缓冲区时,务必使用MDL机制。直接访问用户模式指针会导致系统崩溃,因为在内核模式下这些地址可能是无效的。
3.2 中断请求级别(IRQL)
IRQL虽然不是传统意义上的数据结构,但作为驱动开发中至关重要的概念,它影响着几乎所有内核数据结构的访问方式。
Windows定义了多个IRQL级别:
- PASSIVE_LEVEL (0):普通线程上下文
- APC_LEVEL (1):异步过程调用级别
- DISPATCH_LEVEL (2):调度程序级别
- 各种硬件中断级别(3-31)
关键规则:
- 在DISPATCH_LEVEL及以上级别不能访问分页内存
- 某些内核API只能在特定IRQL级别调用
- 自旋锁的获取会提升IRQL
3.3 对象属性(OBJECT_ATTRIBUTES)
这个结构体用于指定内核对象创建时的各种属性。
c复制typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
常用属性标志:
- OBJ_CASE_INSENSITIVE:名称比较不区分大小写
- OBJ_KERNEL_HANDLE:创建内核模式句柄
- OBJ_PERMANENT:创建永久对象
4. 驱动开发中的数据结构实践
4.1 设备扩展结构设计
在实际驱动开发中,通常需要为每个设备对象关联一个私有的设备扩展结构。这是一种常见的设计模式:
c复制typedef struct _MY_DEVICE_EXTENSION {
ULONG DeviceNumber;
BOOLEAN IsRemoved;
LIST_ENTRY PendingIrpList;
KSPIN_LOCK IrpListLock;
// 设备特定的其他数据
} MY_DEVICE_EXTENSION, *PMY_DEVICE_EXTENSION;
设备扩展通常在创建设备对象时分配:
c复制status = IoCr
