C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构

摘要:
驱动程序通常创建和维护这些数据结构的实例。驱动程序需要在DriverEntry中初始化。让我们首先了解驱动器对象的数据结构。“DriverName:顾名思义,PriveName记录驱动程序的名称。这里使用UNICODE字符串,通常是DriverDriverStartIo:记录StartIo例程的函数地址以进行序列化。”DriverUnload:指定用于驱动程序卸载的回调函数地址DriverObject:指向驱动程序中的专用驱动程序。此外,在驱动程序中,应尽可能避免使用全局变量,因为全局变量涉及不容易同步的问题。

数据结构是计算机程序的核心,I/O管理器定义了一些数据结构,这些数据结构是编写驱动程序时所必须掌握的。驱动程序经常要创建和维护这些数据结构的实例。

一、驱动对象(DRIVER_OBJECT)

每个驱动程序会有唯一的驱动对象与之对应,并且这个驱动对象是在驱动加载的时候,被内核中的对象管理程序所创建的。

驱动对象用DRIVER_OBJECT数据结构表示,它作为驱动的一个实例被内核加载,并且内核对一个驱动只加载一个实例。确切地说,是由内核中的I/O管理器负责加载的。驱动程序需要在DriverEntry中初始化。先了解一下驱动对象的数据结构。

》DeviceObject:每个驱动程序会有一个或多个设备对象。其中,每个设备对象都有一个指针指向下一个驱动对象。通过DeviceObject,就可以遍历驱动对象里的所有设备对象。设备对象是由程序员自己创建的,而非操作系统完成,在驱动被卸载的时候,遍历每个设备对象,并将其删除。

》DriverName:顾名思义,PriveName记录的是驱动程序的名字。这里用UNICODE字符串记录,该字符串一般为Driver[驱动程序名称]。

》HardwareDatabase:这里记录的是设备的硬件数据库键名,这里同样用UNICODE字符串记录。该字符串一般为REGISTRYMACHINEHARDWAREDESCRIPTIONSYSTEM。

》DriverStartIo:记录StartIo例程的函数地址,用于串行化操作。

》DriverUnload:指定驱动卸载时所用的回调函数地址。

》MajorFunction:MajorFunction域记录的是一个函数指针数组,也就是MajorFunction是一个数组,数组中的每个成员记录着一个指针,每一个指针指向的是一个函数。这个函数就是处理IRP的派遣函数。

》FastIoDispatch:文件驱动中用到的派遣函数,参阅MSDN。

二、设备对象(DEVICE_OBJECT)

C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构第1张

每个驱动程序会创建一个或多个设备对象,用DEVICE_OBJECT数据结构表示。每个设备对象都会有一个指针指向下一个设备对象,因此就形成了一个设备链。设备对象保存设备特征和状态的信息。

》DriverObject:指向驱动程序中的驱动独享。同属于一个驱动程序的驱动对象指向的是统一驱动对象。

》NextDevice:指向下一个设备对象。这里指的下一个设备对象是同属于一个驱动对象的设备,也就是同一个驱动程序创建的若干设备对象。每个设备对象根据NextDevice域形成链表,从而可以枚举每个设备对象。

》AttachedDevice:指向下一个设备对象。这里指的是,如果有更高一层的驱动附加到这个驱动的时候,AttachedDevice指向的就是那个更高一层的驱动。

》CurrentIrp:在使用StartIO例程的时候,此域指向的是当前IRP结构。

》Flags:此域是一个32位的无符号整型。每一个位有具体的含义。

标志描述
DO_BUFFERED_IO读写操作使用缓冲方式(系统复制缓冲区)访问用户模式数据
DO_EXCLUSIVE 一次只允许一个线程打开设备句柄
DO_DIRECT_IO读写操作使用直接方式(内存描述符表)访问用户模式数据
DO_DEVICE_INITIALIZING设备对象正在初始化
DO_POWER_PAGABLE必须在PASSIVE_LEVEL级上处理IRP_MJ_PNP请求
DO_POWER_INRUSH   设备上电期间需要大电流

》DeviceExtension:指向的是设备的扩展对象。每个设备都会制定一个设备扩展对象,设备扩展对象记录的是设备自己特殊意义的结构体,也就是程序员自己定义的结构体。另外,在驱动程序中,应该尽量避免全局变量的使用,因为全局变量涉及不容易同步的问题。解决办法,将全局变量存在设备扩展里。

》DeviceType:指明设备的类型。

设备类型描述
FILE_DEVICE_BEEP蜂鸣器设备对象
FILE_DEVICE_CD_ROMCD光驱设备对象
FILE_DEVICE_CD_ROM_FILE_SYSTEMCD光驱文件系统设备对象
FILE_DEVICE_CONTROLLER控制器设备对象
FILE_DEVICE_DATALINK数据链设备对象
FILE_DEVICE_DFSDFS设备对象
FILE_DEVICE_DISK磁盘设备对象
FILE_DEVICE_DISK_FILE_SYSTEM磁盘文件系统设备对象
FILE_DEVICE_FILE_SYSTEM文件系统设备对象
FILE_DEVICE_INPORT_PORT输入端口设备对象
FILE_DEVICE_KEYBOARD键盘设备对象
FILE_DEVICE_MAILSLOT邮件槽设备对象
FILE_DEVICE_MIDI_INMIDI输入设备对象
FILE_DEVICE_MIDI_OUTMIDI输出设备对象
FILE_DEVICE_MOUSE鼠标设备对象
FILE_DEVICE_MULTI_UNC_PROVIDER多UNC设备对象
FILE_DEVICE_NAMED_PIPE命名管道设备对象
FILE_DEVICE_NETWORK网络设备对象
FILE_DEVICE_NETWORK_BROWSER网络浏览器涉笔对象
FILE_DEVICE_NETWORK_FILE_SYSTEM网络文件系统设备对象
FILE_DEVICE_NULL空设备对象
FILE_DEVICE_PARALLEL_PORT并口设备对象
FILE_DEVICE_PHYSICAL_NETCARD物理网卡设备对象
FILE_DEVICE_PRINTER打印机设备对象
FILE_DEVICE_SCANNER扫描仪设备对象
FILE_DEVICE_SERIAL_MOUSE_PORT串口鼠标设备对象
FILE_DEVICE_SERIAL_PORT串口设备对象
FILE_DEVICE_SCREEN屏幕设备对象
FILE_DEVICE_SOUND声音设备对象
FILE_DEVICE_STREAMS流设备对象
FILE_DEVICE_TAPE磁带设备对象
FILE_DEVICE_TAPE_FILE_SYSTEM磁带文件系统设备对象
FILE_DEVICE_TEANSPORT传输设备对象
FILE_DEVICE_UNKNOWN未知设备对象
FILE_DEVICE_VIDEO视频设备对象
FILE_DEVICE_VIRTUAL_DISK虚拟磁盘设备对象
FILE_DEVICE_WAVE_OUT声音输出设备对象
FILE_DEVICE_8042_PORT8042端口设备对象
FILE_DEVICE_NETWORK_REDIRECTOR网卡设备对象
FILE_DEVICE_BATTERY电池设备对象
FILE_DEVICE_BUS_EXTENDER总线扩展设备对象
FILE_DEVICE_MODEM调制解调器设备对象
FILE_DEVICE_VDMVDM设备对象
FILE_DEVICE_MASS_STORAGE大容量存储设备对象
FILE_DEVICE_SMBSMB设备对象
FILE_DEVICE_KS内核流设备对象
FILE_DEVICE_CHANGER充电设备对象
FILE_DEVICE_SMARTCARD智能卡设备对象
FILE_DEVICE_ACPIACPI设备对象
FILE_DEVICE_DVDDVD设备对象
FILE_DEVICE_WAVE_IN声音输入设备对象

根据设备的需要,需要填写相应的设备类型。当制作虚拟设备时,应选择FILE_DEVICE_UNKNOWN类型的设备。

》StackSize:在多层驱动情况下,驱动与驱动之间会形成类似堆栈的结构。IRP会依次从最高层传递到最底层。StackSize描述的就是这个层数。

》AlignmentRequirement:设备在大容量传输的时候,需要内存对齐,以保证传输速度。

三、设备扩展

设备对象记录“通用”设备的信息,而另外一些“特殊”信息记录在设备扩展里。各个设备扩展由程序员自己定义,每个设备的设备扩展也不尽相同。设备扩展是由指定内容和大小,由I/O管理器创建的,并保存在非分页内存中。

在驱动程序中,尽量避免使用全局函数,因为全局函数往往导致函数的不可重入性。重入性指的是,在多线程的程序中,多个函数并行运行,函数的运行结果不会根据函数的调用先后顺序而导致不同。解决的办法是,将全局变量以设备扩展的形式存储,并加以适当的同步保护措施。除此之外,在设备扩展中,还会记录下列一些内容:

》设备对象的反向指针。

》设备状态或驱动环境信息。

》中断对象指针。

》控制器对象指针。

由于设备扩展是驱动程序专用的,它的结构必须在驱动程序的头文件中定义。

免责声明:文章转载自《C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇C#使用Log4Net记录日志JDBC: C3P0连接池下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C字符串和指针问题汇总

空指针和传参问题 1) 段错误。形参改为二级指针即可 void GetMemory( char *p ){   p = (char *) malloc( 100); } void Test( void){ char *str =NULL; GetMemory( str ); strcpy( str, "hello world"); prin...

Java关于数组对象赋值与指针

在实现PageRank算法中犯了两个错误, 1:在对double类型赋值时,除法中没有将分母设置为double类型,而是int类型,导致出现商为0的结果错误,导致推迟 2:当定义两个数组时,对数组赋值时,忘记了,数组是对象的特点直接nowRank=resultRank; 其中resultRank又重新赋值,所以导致nowRank中元素值也发生变化,因为数组...

RedisTemplate访问Redis数据结构(五)——ZSet

Redis 有序集合和无序集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。有序集合的成员是唯一的,但分数(score)却可以重复。redis正是通过分数来为集合中的成员进行从小到大的排序。 ZSetOperations提供了一系列方法对有序集合进行操作。首先初始化spring工厂获得redis...

redis数据结构详解之Hash(四)

序言Hash数据结构累似c#中的dictionary,大家对数组应该比较了解,数组是通过索引快速定位到指定元素的,无论是访问数组的第一个元素还是最后一个元素,所耗费的时间都是一样的,但是数组中的索引却没有实际意义,他只是一个位置而已。而我们在查找某个元素时,一般都会使用有意义的字段来做索引,这就产生啦dictionary。其实dictionary的实现,就...

标准C程序设计七---33

Linux应用 编程深入 语言编程标准C程序设计七---经典C11程序设计以下内容为阅读:《标准C程序设计》(第7版) 作者:E. Balagurusamy(印), 李周芳译 清华大学出版社 2017.7《21天学通C语言》(第7版) 作者:Bradley Jones Peter Aitken Dean Miller(美), 姜佑译 人民邮电出版社 201...

转:Delphi中使用比较少的一些语法

http://www.cnblogs.com/Murphieston/p/5577836.html本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要。 ---Murphy 1,构造和析构函数: a,构造函数: 一般基于TComponent组件的派生类,都应该使用overload关键字进行继承,Delp...