热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

驱动程序的基本结构

驱动程序的基本结构1、Windows驱动程序中重要的数据结构1.1、驱动对象(DRIVER_OBJECT)每个驱动程序会有唯一的驱动对象与之对应,并且这个驱动对象是在驱

驱动程序的基本结构

1、Windows驱动程序中重要的数据结构

1.1、驱动对象(DRIVER_OBJECT)

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

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

typedef struct _DRIVER_OBJECT

{

        CSHORT      Type;

        CSHORT      Size;

        PDEVICE_OBJECT DeiceObject;//对应的设备对象,为链表

        ULONG Flags;

        PVOID DriverStart;

        ULONG DriverSize;

        PVOID DriverSection;

        PDRIVER_EXTENSION DriverExtension;

        UNICODE_STRING DriverName;//该字符一般为\Driver\[驱动程序名称]

        PUNICODE_STRING HardwareDataBase;//记录设备的硬件数据库键名,值一般指向注册表位置的字符串

        PFAST_IO_DISPATCH FastIoDispatch;//文件驱动中用到的派遣函数

        PDRIVER_INITIALIZE DriverInit;

        PDRIVER_STARTIO DriverStartIo;//记录StartIO例程的函数地址,用于串行化操作

        PDRIVER_UNLOAD DriverUnload;//用于卸载驱动

        PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION]; //记录的是一个函数指针数组,其每个成员都指向的是一个IRP派遣函数

}DRIVER_OBJECT;

typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;

1.2、设备对象(DEVICE_OBJECT)

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

typedef struct _DEVICE_OBJECT {

   CSHORT  Type;

   USHORT  Size;

   LONG  ReferenceCount;

   PDRIVER_OBJECT  DriverObject;

   PDEVICE_OBJECT  NextDevice;//指向下一个同属于一个驱动对象的设备对象

   PDEVICE_OBJECT  AttachedDevice;//指向更高一层的驱动的设备对象

   PIRP  CurrentIrp;//在使用StartIO例程的时候,此域指向的是当前的IRP结构

   PIO_TIMER  Timer;

   ULONG  Flags;//标志位

   ULONG  Characteristics;

   __volatile PVPB  Vpb;

   PVOID  DeviceExtension;

   DEVICE_TYPE  DeviceType;//指明设备的类型

   CCHAR  StackSize;//在多层驱动情况下,驱动与驱动之间会形成类似堆栈的结构。IRP会依次从最高层穿戴到最底层。StackSize表示层数

   union {

     LIST_ENTRY   ListEntry;

     WAIT_CONTEXT_BLOCK   Wcb;

   } Queue;

   ULONG  AlignmentRequirement;//设备在大容量传输的时候,需要内存对其,保证传输速度

   KDEVICE_QUEUE  DeviceQueue;

   KDPC  Dpc;

   ULONG  ActiveThreadCount;

   PSECURITY_DESCRIPTOR  SecurityDescriptor;

   KEVENT  DeviceLock;

   USHORT  SectorSize;

   USHORT  Spare1;

   PDEVOBJ_EXTENSION  DeviceObjectExtension;

   PVOID  Reserved;

} DEVICE_OBJECT, *PDEVICE_OBJECT;

 

1.3、设备扩展

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

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

 

 

2、NT式驱动的基本结构

对于NT式驱动来说,主要的函数是DriverEntry例程、卸载例程及各个IRP的派遣例程

2.1、驱动加载过程与驱动入口函数(DriverEntry

DriverEntry主要是对驱动程序进行初始化工作,它是由系统进程所调用。在WIndows中有个特殊的进程叫做系统进程(System),系统进程在系统启动的时候就已经被创建了。

驱动加载的时候,系统进程启动新的线程,调用执行体组件中的对象管理器,创建一个驱动对象。这个驱动对象时一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组件中的配置管理程序,查询此驱动程序对应的注册表中的项。

系统线程调用驱动程序的DriverEntry例程时,同时传进两个参数(pDriverObjectpRegistryPath)。

DriverEntry参数的修饰:“IN”、“OUT”、“INOUT”在DDK中都被定义成空串,它们的功能类似于程序注释,当看到一个IN参数时,应该认为该参数是纯粹用于输入目的。“OUT”参数代表这个参数仅用于参数的输出参数。“INOUT”用于既可以输入又可以输出的参数。

2.2、创建设备对象

NT式驱动中,创建设备对象是由IoCreateDevice内核函数完成的:

NTSTATUS 
  IoCreateDevice(
    IN PDRIVER_OBJECT  DriverObject,//指向驱动对象的指针
    IN ULONG  DeviceExtensionSize,//指定设备扩展的大小
    IN PUNICODE_STRING  DeviceName  OPTIONAL,//设备对象的名字
    IN DEVICE_TYPE  DeviceType,
    IN ULONG  DeviceCharacteristics,//设备对象的特征
    IN BOOLEAN  Exclusive,//设备对象是否为内核模式下使用,一般为TRUE
    OUT PDEVICE_OBJECT  *DeviceObject//I/O管理器负责创建这个设备对象,并返回对象地址
    );

设备名称用UNICODE字符串指定,并且字符串必须是“\Device\[设备名]”的形式。在Windows下所有设备都是以类似名字命名的。例如,磁盘分区的C盘、D盘、E盘、F盘就被命名为“\Device\HarddiskVolume1”、“\Device\HarddiskVolume2”、“\Device\HarddiskVolume3”、“\Device\HarddiskVolume4”。

当然,也可以不指定设备的名字,如果不指定,I\O管理器会自动分配一个数组作为设备的设备名,如:“\Device\00000001”。如果指定了设备名,只能被内核模式下的其他驱动所识别。但是在用户模式下的应用程序无法识别这个设备。让用户模式下的应用程序能识别设备有两种方法,第一种是通过符号链接找到设备,第二种是通过设备接口找到设备。设备接口的办法在NT驱动很少使用。

符号链接可以理解为设备对象起的一个别名。设备对象的名称只能被内核模式下驱动识别,而别名也可以被用户模式下的应用程序识别。例如,常说的C盘,D盘就是符号链接。创建符号链接的函数:

NTSTATUS 
  IoCreateSymbolicLink(
    IN PUNICODE_STRING  SymbolicLinkName,//符号链接的字符串
    IN PUNICODE_STRING  DeviceName//设备对象的字符串
    );

在内核模式下,符号链接是以“\??\”开头的(或者是“\DosDevice\”开头的),如C盘就是“\??\C(或者“\DosDevice\C”),而在用户模式下,则是以”\\.\”开头的,如C盘就是”\\.\C”。

//创建设备对象
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject){

NTSTATUS status;

PDEVICE_OBJECT pDevObj;

PDEVICE_EXTENSION pDevExt;

//创建设备名称

UNICODE_STRING devName;

RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

//创建设备

status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&(UNICODE_STRING)devName,FILE_DEVICE_UNKONOWN,0,TRUE,&pDevObj);

//创建设备对象是否创建成功

if(!NT_SUCCESS(status))

return status;

//将设备设置为缓冲区设备

pDevObj->Flags!=DO_BUFFERED_IO;

//得到设备扩展

pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

//设置设备扩展中的设备名称

pDeviExt->ustrDeviceName = devName;

//创建符号链接

UNICODE_STRING symLinkName;

RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");

//创建符号链接

status = IoCreateSymbolicLink(&symLinkName,&devName);

//判断是否成功创建符号链接

if(!NT_SUCCESS(status))

{

//删除符号链接

IoDeleteDevice(pDevObj);

return status;

}

return STATUS_SUCCESS;

}

设备类型为FILE_DEVICE_UNKNOWN说明此设备是常用设备之外的设备,一般虚拟设备常使用这个作为设备类型

 

2.3DriverUnload例程

在驱动对象中会设置DriverUnload例程,此例程在驱动被卸载的时候调用。在NT式驱动中,DriverUnload一般负责删除在DriverEntry中创建的设备对象,并且将设备对象所关联的符号链接删除。另外,DriverUnload还负责对一些资源进行回收。

VOID 
  IoDeleteDevice(
    IN PDEVICE_OBJECT  DeviceObject
    );

DriverUnload中,除了要删除设备对象,同时,还要对设备对象关联的符号链接进行删除。删除符号链接的函数是IoDeleteSymbolicLink

NTSTATUS 
  IoDeleteSymbolicLink(
    IN PUNICODE_STRING  SymbolicLinkName//已经被注册了的符号链接
    );

3WDM式驱动的基本结构

3.1、物理设备对象与功能设备对象

WDM模型中,完成一个设备的操作,至少有两个设备对象共同完成。其中,一个是物理设备对象(Physical Device Object——PDO),另外一个是功能设备对象(Function Device Object——FDO)。其关系是“附加”与“被附加”的关系

PC插入某个设备的时候,PDO会自动创建。确切的说,是由总线驱动创建的。PDO不能单独操作设备,需要配合FDO一起使用。系统会提示检测到新设备,要求安装驱动程序。需要安装的驱动程序指的是WDM程序,此驱动程序负责创建FDO,并附加到PDO上。

3.2WDM驱动的入口程序

NT驱动一样,WDM驱动的入口程序也是DriverEntry,但是初始化作用被分散到其他例程中。例如,创建设备对象的责任就被放在AddDevice例程。

WDMNT式驱动的DriverEntry有以下几点不同:

a)、增加了对AddDevice函数的设置。这是WDM驱动和NT驱动非常重要的不同点。因为NT驱动是主动加载设备的,也就是驱动一旦加载就创建设备。而WDM驱动是被动加载设备的,操作系统必须加载PDO以后,调用驱动的AddDevice例程,AddDevice例程中负责创建FDO,并且附加到PDO之上。

b)、创建设备对象已经不再这个函数中了,而在AddDevice例程中创建。

c)、必须加入IRP_MJ_PNP的派遣回调函数。IRP_MJ_PNP主要是负责计算机中即插即用的处理,在WDM驱动中加入了很多即插即用的处理。

3.3WDM驱动的AddDevice例程

a)、通过IoCreateDevice创建设备对象FDO

b)、将FDO地址保存在设备扩展中

c)、将FDO使用IoAttachDeviceToDeviceStack函数附加到PDO

d)、设置FDOFlags子域

3.4、DriverUnload例程

在NT式驱动中,DriverUnload例程主要负责做删除设备和取消符号链接。而在WDM驱动中,这部分操作被IRP_MN_REMOVE_DEVICE这个IRP的处理函数负责。IRP_MN_REMOVE_DEVICE这个IRP是当设备需要被卸载的时候,由即插即用管理器创建,并发送到驱动程序中的。IRP一般由两个号码指定该IRP的具体意义,一个是主IRP号,另一个是辅IRP号。每个IRP都由对应的派遣函数所处理。

4、小结

本章重点介绍了NT式驱动和WDM驱动的结构,从中可以看出WDM驱动程序是在NT驱动程序的基础上变化过来的,可以将WDM驱动程序看成是NT驱动程序的特例。同时,还介绍了驱动程序的入口函数,卸载函数,IRP的派遣函数等。



以上内容参考自张帆 史彩成等编著的《Windows 驱动开发技术详解》第四

 

驱动程序的基本结构,布布扣,bubuko.com


推荐阅读
  • 解决网页乱码问题的实用方法
    网页乱码问题在开发中较为常见,主要由文件编码、程序字符集设置和数据库连接字符集设置不当引起。本文将详细介绍如何逐一排查并解决这些问题。 ... [详细]
  • Java EE 平台集成了多种服务、API 和协议,旨在支持基于 Web 的多层应用程序开发。本文将详细介绍 Java EE 中的 13 种关键技术规范,帮助开发者更好地理解和应用这些技术。 ... [详细]
  • 2023年最新指南:如何在PHP中屏蔽警告和错误
    本文详细介绍了如何在PHP中屏蔽警告和错误,包括多种方法和最佳实践,帮助开发者提升代码质量和安全性。 ... [详细]
  • 本文将详细探讨PHP中C的作用,并对比其他编程语言如Java和C的特点及其适用场景。 ... [详细]
  • 申请地址:https://developer.apple.com/appstore/contact/?topic=expedite 常见申请理由:1. 我们即将发布新产品,这是一个媒体活动,我们无法承担任何风险,因此在多个方面努力提升应用质量。 ... [详细]
  • 使用 Mui.js 获取复选框值的方法
    本文介绍如何使用 Mui.js 框架来获取复选框的值,并通过数组进行处理和展示。 ... [详细]
  • Gty的二逼妹子序列 - 分块与莫队算法的应用
    Autumn 和 Bakser 正在研究 Gty 的妹子序列,但遇到了一个难题。他们希望计算某个区间内美丽度属于 [a, b] 的妹子的美丽度种类数。本文将详细介绍如何利用分块和莫队算法解决这一问题。 ... [详细]
  • 阿里云 Aliplayer高级功能介绍(八):安全播放
    如何保障视频内容的安全,不被盗链、非法下载和传播,阿里云视频点播已经有一套完善的机 ... [详细]
  • SvpplyTable: 实现可扩展和可折叠的菜单动画
    SvpplyTable 是一个示例项目,旨在实现类似 Svpply 应用程序中的可扩展和可折叠的菜单动画效果。该项目托管在 GitHub 上,地址为 https://github.com/liuminqian/SvpplyTable。 ... [详细]
  • 本文整理了一份基础的嵌入式Linux工程师笔试题,涵盖填空题、编程题和简答题,旨在帮助考生更好地准备考试。 ... [详细]
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • PHP 5.5.31 和 PHP 5.6.17 安全更新发布
    PHP 5.5.31 和 PHP 5.6.17 已正式发布,主要包含多个安全修复。强烈建议所有用户尽快升级至最新版本以确保系统安全。 ... [详细]
  • 本文详细介绍了 Spark 中的弹性分布式数据集(RDD)及其常见的操作方法,包括 union、intersection、cartesian、subtract、join、cogroup 等转换操作,以及 count、collect、reduce、take、foreach、first、saveAsTextFile 等行动操作。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 本文讨论了在进行 MySQL 数据迁移过程中遇到的所有 .frm 文件报错的问题,并提供了详细的解决方案和建议。 ... [详细]
author-avatar
mobiledu2502891487
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有