0.driverbase-WDM和NT驱动
来源:互联网 发布:mac os sierra beta4 编辑:程序博客网 时间:2024/04/25 11:42
WDM要导入的的头文件是WDM.h
和NT式驱动程序一样,入口函数同样是DriverEntry,且在C++编译的时候需要用extern"C"修饰
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = pDriverObject->MajorFunction[IRP_MJ_CREATE] = pDriverObject->MajorFunction[IRP_MJ_READ] = pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;pDriverObject->DriverUnload = HelloWDMUnload;
和NT驱动不同在于WDM多了一个叫个AddDevice的回调
这个回调来自DriverExtension->AddDevice中,此回调的作用是创建设备对象并由PNP(即插即用)管理器调用.
同时,通过MajorFuntion[IRP_MJ_PNP]设置了PNP的IRP处理,也是和NT驱动不同点之一
所以,在WDM的驱动程序中,创建设备对象的任务不再由DriverEntry来承担,
而是需要驱动程序向系统注册一个称为AddDevice的函数,此函数由PNP管理器负责调用,主要就是创建设备对象,函数原型如下:
NTSTATUSDRIVER_ADD_DEVICE ( __in struct _DRIVER_OBJECT *DriverObject, __in struct _DEVICE_OBJECT *PhysicalDeviceObject );
第一个是driverentry传过来的驱动对象,
第二个是PNP管理器传进来的底层驱动设备对象,这个概念在NT驱动是没有的
下面来看下这个函数:
#define PAGED_CODE() { \ if (KeGetCurrentIrql() > APC_LEVEL) { \ KdPrint(("EX: Pageable code called at IRQL %d\n", KeGetCurrentIrql())); \ PAGED_ASSERT(FALSE); \
也就是IRQL超过了APC_LEVEL时,在check版中会产生一个断言,断言会使程序终止,
下面解释下IRQL:
IRQL是Interrupt ReQuest Level 中断请求级别。一个由windows虚拟出来的概念,划分在windows下中断的优先级
IRP是I/O Request Packet I/O请求包
IRQL是分0-31级的,中断的意思就是正常运行的程序被打断,而转向做其他事,这个有个level,就是说程序如果level大于或等于中断源的level,它就可以无视中断源(官大一级压死人,平起平坐也不用鸟你)
引入一些小概念:
引起中断的事件称为中断源。
中断源向CPU提出处理的请求称为中断请求。
发生中断时被打断程序的暂停点称为断点。
CPU暂停现行程序而转为响应中断请求的过程称为中断响应。
处理中断源的程序称为中断处理程序
一般level如下:
PASSIVE_LEVEL:
线程即运行在该中断级别上,它对所有中断都作出响应。用户模式代码都是运行在该中断级别上。
APC_LEVEL:
当I/o 操作完成时,系统会产生这个中断。为了响应这个中断,应用程序向I/O 完成中断处理例程队列中插入一个APC回调函数,当I/O完成时,该APC函数被调用。如果你不想对这个中断做出响应,你就可以将IRQL提升至APC_LEVEL。这时当I/O完成时,应用程序将不会受到该中断信号。可以调用KeEnterCriticalRegion或KeEnterGuardedRegion来将IRQL提升至该级别。APC中断通常是由处理器引发,可以想自己发出也可是像其他处理器发出。一般情况下,是不应该的使用的APC_LEVEL的,除非你想使用Fast Mutexes之类的东西。
DISPATCH_LEVEL:
为了能够执行多任务,系统必须允许线程调度。而线程调度的根本就是靠时钟中断来保证的,该级别的中断即调度中断。当你的代码运行的IRQL被提升为DISPATH_LEVEL时,就意味着你的代码不在受线程中断影响力。你的代码会一直运行直到你将IRQL设置为低于DISPATH_LEVEL为止。这中间如果发生缺页错误之类的IRQL级别在DISPATH_LEVEL之下的严重中断时,这些中断均不会被处理。这时,代码将无法正常运行。所以,DISPATH_LEVEL的使用绝对要慎之又慎。只有在是自旋锁时,你才应该考虑选择该IRQL。
相对于NT驱动,扩展结构中多了一个参数:
typedef struct _DEVICE_EXTENSION{ PDEVICE_OBJECT fdo; PDEVICE_OBJECT NextStackDevice;UNICODE_STRING ustrDeviceName;// 设备名UNICODE_STRING ustrSymLinkName;// 符号链接名} DEVICE_EXTENSION, *PDEVICE_EXTENSION;NextStackDevice!
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
IoAttachDeviceToDeviceStack函数简介:
如果一个设备被其他设备绑定,它们在一起的一组设备,被称为设备栈,这个函数返回了最终被绑定的设备指针,也就是最顶层设备
MSDN:The IoAttachDeviceToDeviceStack routine attaches the caller's device object to the highest device object in the chain and returns a pointer to the previously highest device object.(把调用者设备对象加到设备对象链的最上层,同时返回原来最上层的设备对象)
比如原来有一个Device A,在这个Device的上层有一个过滤驱动程序的设备Device B,
可以用这个图表示:
+---+
| B | (最上层)
+---+
| A |
+---+
然后我们有一个新的Device C,用IoAttachDeviceToDeviceStack(C, A)之后,就把C放到了A所在的链的末端,如下:
+---+
| C | (最上层)
+---+
| B | (次上层,原来的最上层)
+---+
| A |
+---+
同时返回B(原来的最上层)。
下面看下HelloWDM处理PNP的回调函数
其中,IRP_MJ_PNP会细分为若干个字类,如,IRP_MN_START_DEVICE,IRP_MN_REMOVE_DEVICE,IRP_MN_STOP_DEVICE
首先用:
PAGED_CODE();
保证运行在低于APC_LEVEL的中断优先级的级别上
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;得到设备的扩展结构
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);得到当前IRP的堆栈
IRP可以看成是窗口程序中的消息(Message),DEVICE_OBJECT可以看成是窗口程序中的窗口(Window)
任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的 IO_STACK_LOCATION 结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序,另外还有一个堆栈单元供IRP的创建者使用,如下图:
io_stack_location的结构如下图:
继续上面的代码:
ULONG fcn = stack->MinorFunction;
fcn指定了IRP_MN_START_DEVICE,IRP_MN_QUERY_REMOVE_DEVICE,IRP_MN_REMOVE_DEVICE
static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) = {DefaultPnpHandler,// IRP_MN_START_DEVICEDefaultPnpHandler,// IRP_MN_QUERY_REMOVE_DEVICEHandleRemoveDevice,// IRP_MN_REMOVE_DEVICEDefaultPnpHandler,// IRP_MN_CANCEL_REMOVE_DEVICE
不在上述列表中的fcn,默认调DefaultPnpHandler函数:
if (fcn >= arraysize(fcntab)){// 未知的子功能代码status = DefaultPnpHandler(pdx, Irp); // some function we don't know aboutreturn status;}
否则调对应列表中的函数:
status = (*fcntab[fcn])(pdx, Irp);
可以看下MajorFunction的类型:
UCHAR MajorFunction;typedef unsigned char UCHAR;最多也就255种
下面看下卸载:
HandleRemoveDevice,// IRP_MN_REMOVE_DEVICE而原来的外部卸载函数是没有东西的:
pDriverObject->DriverUnload = HelloWDMUnload;
#pragma PAGEDCODEvoid HelloWDMUnload(IN PDRIVER_OBJECT DriverObject){PAGED_CODE();KdPrint(("Enter HelloWDMUnload\n"));KdPrint(("Leave HelloWDMUnload\n"));}
所以直接卸载函数是HandleRemoveDevice:
#pragma PAGEDCODENTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp){PAGED_CODE();KdPrint(("Enter HandleRemoveDevice\n"));Irp->IoStatus.Status = STATUS_SUCCESS;//设置虎IRP状态为顺利完成NTSTATUS status = DefaultPnpHandler(pdx, Irp);//调用默认的PNP的IRP的处理函数IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);//删除此设备对象的符号链接 //调用IoDetachDevice()把fdo从设备栈中脱开: if (pdx->NextStackDevice) IoDetachDevice(pdx->NextStackDevice); //删除fdo: IoDeleteDevice(pdx->fdo);//删除设备对象.KdPrint(("Leave HandleRemoveDevice\n"));return status;}
- 0.driverbase-WDM和NT驱动
- 0.driverbase-WDM驱动在win7和XP下系统方式加载
- NT式驱动和WDM驱动区别
- NT式驱动和WDM式驱动程序
- 区分 WDM驱动和NT驱动(有待继续思考)
- 0.driverbase-驱动对象、设备对象、DriverEntry、IoCreateDevice、符号链接、DriverUnLoad、WDM
- windows 下驱动NT和WDM 基本例子 (一)
- NT式和WDM式驱动程序模型
- NT驱动程序和WDM驱动程序的区别
- WDM驱动改可手动加卸载的NT驱动
- 驱动编程(一),NT - WDM - WDF 驱动概念
- 0.driverbase-prefmon查看驱动性能
- 0.driverbase-makefile和source简单语法
- 0.driverbase-驱动编译环境--x86 Checked Build Environment
- WDM驱动
- WDM驱动和应用的交互
- WDM和WDF usb驱动不同点
- 使用WDM驱动实现在NT下读取物理端口,特殊寄存器,物理内存的代码(C++驱动加载代码)
- MediaPlayer_视频播放器
- WIN8系统如何分区
- STL string
- zoj 3726 Pocket Cube(搜索)
- assert() 宏用法
- 0.driverbase-WDM和NT驱动
- java学习02-java基础知识、基本数据类型、运算符
- [Android开发实战]金山清理大师(猎豹清理大师)一键加速快捷方式动画实现
- ACE_Task介绍(生产者/消费者)v3.0
- Dijkstra算法的个人理解
- Qt实现监听功能
- PokerGame
- WPF之布局控件
- ZOJ 3735 Josephina and RPG DP