Windows 驱动模型
驱动种类
驱动程序的种类有总线驱动程序(bus driver)、功能驱动程序(function driver)、筛选驱动程序(filter driver)三种:
- 总线驱动程序(bus drivers)
- 虚拟总线驱动程序(Virtual bus drivers):调用IoInvalidateDeviceRelations通知PnP Manager。
- 功能驱动程序(function drivers)
- 微端口型驱动程序(miniport drivers):属于USB, Audio, SCSI 以及 network 的转换器(adapters)。
- 虚拟设备驱动程序(Virtual device drivers)
- 类型驱动程序(Class drivers)
- 筛选性驱动程序(filter drivers)
- 上层筛选性驱动程序(upper-filter drivers)
- 下层筛选性驱动程序(lower-filter drivers)
[编辑] 程序进入点
如同许多语言从main函数开始,WDM驱动程序的加载(Loading)乃至于动作,以及其卸载(Unload)行为,都有其步骤与规则。WDM驱动程序可以动态式的加载与卸载,当侦测到设备(Device)插入的时候,依据“PnP Manager”会自动地加载相对应的设备驱动程序,然后成为“Driver Object”,并调用DriverEntry函数。所有的WDM驱动程序,都必须拥有这个DriverEntry例程(routine),而且“此一名称不可改变”的,所有的驱动程序也是从这里开始运行的,I/O Manager首先调用驱动程序的
DriverEntry()
。DriverEntry在DDK Compiler编译出来的输出符号表是“DriverEntry@8
”,原因是微软的C编译器把stdcall函示(例如VcDCall)的名称加上“记号”,加上一个@符号,在附上参数的总字节个数,8指此function的所有参数所占的byte数。DriverEntry函数有两个参数,其中第一个参数
PDRIVER_OBJECT DriverObject
是指向该驱动程序对应的对象指针;PUNICODE_STRING RegistryPath
,驱动程序的服务主要键码,这个参数的使用时机并不多。以下是一个简单而标准的DriverEntry基本实现:NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pRegistryString) { //PDEVICE_OBJECT DriverObject; UNICODE_STRING deviceName; RtlInitUnicodeString( &deviceName, DEVICE_NAME ); status = IoCreateDevice( DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, true, &pDeviceObj ); UNICODE_STRING linkName; RtlInitUnicodeString( &linkName, LINK_NAME ); status = IoCreateSymbolicLink( &linkName, &deviceName ); DriverObject->DriverUnload = DriverUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = xxDriverDispatch; DriverObject->DriverUnload = xxUnload; return STATUS_SUCCESS; }[编辑] IRQL
DriverEntry运作在IRQL = PASSIVE_LEVEL等级,所以可以使用标签页内存。WDM有数个经常实现的PASSIVE_LEVEL等级如下:
- DriverEntry
- Dispatch Function:DispatchXxx
- Unload:UnloadXxx
- AddDevice: XxxAddDevice
- Reinitialize: XxxReinitialize
另外还有几个DISPATCH_LEVEL等级的函数如下:
- StartIo
- AdapterControl
- ControllerControl
- IoTimer
- Dpc(延迟程序调用,Deferred Procedure Call)
[编辑] 派送进程
当DriverEntry函数完成对象的初始化与系统注册之后,接下来的重点便会落在Dispatch Function身上。每当I/O Manager得到一个请求时(例如按键,移动鼠标),它使用请求的函数代码(IoControlCode)调用驱动程序中几个Dispatch进程。
NTSTATUS xxDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp) { NTSTATUS ntStatus = STATUS_SUCCESS; ULONG IoControlCodes = 0; PIO_STACK_LOCATION IrpStack=NULL; pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; IrpStack = IoGetCurrentIrpStackLocation(pIrp); switch (IrpStack->MajorFunction) { case IRP_MJ_CREATE: break; case IRP_MJ_CLOSE: break; case IRP_MJ_DEVICE_CONTROL: IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode; switch (IoControlCodes) { case IOCTL_1: break; case IOCTL_2: break; default: pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; break; } break; default: break; } ntStatus=pIrp->IoStatus.Status; IoCompleteRequest(pIrp,IO_NO_INCREMENT); return ntStatus; }[编辑] 卸载
Unload负责在驱动程序被停止前做一些必要的处理动作,如释放资源,记录最后状态等。
VOID DriverUnLoad (IN PDRIVER_OBJECT DriverObject) { if (DeviceObject) { IoUnregisterFileSystem(DeviceObject); IoDeleteDevice(DeviceObject); //DeviceObject = 0; }[编辑] 编译
驱动程序的编译需要使用DDK(Device Driver Kit)中的build指令,它是一道命令行程序,一般会在后面加上参数:-ceZ。例如:
C:\driver sample>build.exe -ceZDDK可用于建立用于 Windows 2000、Windows XP、Microsoft Windows Server 2003、Vista的建置环境,但在Windows操作系统并非缺省的功能,必须另行安装。安装完成后你会看到Build Enviroment,free是指release版,check则是debug版。
- Windows XP checked 64 Bit Build Environment
- Windows XP checked Build Environment
- Windows XP free 64 Bit Build Environment
- Windows XP free Build Environment
build指令一开始调用Build.exe编译链接器,从系统“环境变量”(Environment Variable)Include中得到引用文件的地址,然后调用Visual C++的编译链接器Nmake.exe进行实际的编译链接工作。在编译过程中遇到的错误,遇到的警告,会记录到buildxxx.log,buildxxx.wrn,buildxxx.err等文件中。
[编辑] 安装
安装过程分成两个步骤:
- 首先将编译成的.sys文件复制到Windows NT的System32\\Drivers\\目录下;
- 接着在Registry的HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\下建立和.sys文件同名的键,然后在之下建立名为Start、Type、ErrorControl的三种REG_DWORD类型的键值。
[编辑] 示例
Microsoft DDK提供了大量的WDM示例(samples)参考,这些示例随着DDK的安装,会进驻我们的系统之中(WINDDK\xx00\src)。一般人不大可能从轮子造起一个新的驱动程序,大部份要靠“既有的示例”来改良一个新的驱动程序。
- 1394
- Audio
- AVStream
- filesys
- general
- hid
- input
- ir
- kernel
- WDM 学习曲线过长。
- 电源管理(power management)事件与随插即用(Plug-and-play)差异太大。导致系统的睡眠(sleep)与清醒(wake up)状态容易产生问题。
- I/O cancellation 几乎不可能达成。
- 每支驱动程序动辄要撰写数千行的代码。
- 不支持纯用户模式(pure user-mode)驱动程序。