520 likes | 906 Views
Windows 驱动开发. 新产品开发部 郑然 2005.10.18. 讲解内容. Windows XP 系统简介 Windows Driver Model 工作机制 处理 IRP 的标准模型 WDM 中的 HelloWorld 程序 Windows XP 的 IO 流程 用户程序和驱动程序的交互 ——IOCTL 内核调试器 SoftIce. Windows XP 简介. Windows XP 的特性. 支持对称多处理器 基于对象 支持抢占式多任务 异步 IO. Windows XP 的系统结构图. Windows XP 的系统组件.
E N D
Windows驱动开发 新产品开发部 郑然 2005.10.18
讲解内容 Windows XP系统简介 Windows Driver Model 工作机制 处理IRP的标准模型 WDM中的HelloWorld程序 Windows XP的IO流程 用户程序和驱动程序的交互——IOCTL 内核调试器SoftIce
Windows XP的特性 • 支持对称多处理器 • 基于对象 • 支持抢占式多任务 • 异步IO
Windows XP的系统组件 • 硬件抽象层(HAL):硬件抽象层隐藏各种与硬件相关的细节,例如IO接口、中断控制器以及多处理器通信机制等硬件信息,HAL为不同的硬件提供了统一的接口。 • 内核:内核是整个操作系统的神经中枢,内核的目标之一是提供一个严格定义的,可预测的操作系统基本要素和机制的低级操作。 • 执行体:图中的暗纹部分都属于执行体,但它们是完全独立的,只通过很好定义的接口来通讯 。
Windows XP驱动程序分类(1/2) • Windows XP中存在很多种驱动程序,如图
Windows XP驱动程序分类(2/2) • 虚拟设备驱动程序(VDD):VDD是一种用户态组件,用于基于MS-DOS的应用程序访问Inter X86体系结构的硬件。 • 文件系统驱动程序:实现Windows XP中的文件系统。 • 遗留设备驱动程序:是一种不需要其他驱动程序帮助的直接访问控制硬件的驱动程序,其中大部分是原来windows NT中的驱动程序,不加修改的放在Windows XP中。 • PnP驱动程序是一种遵循即插即用协议的驱动程序。WDM驱动程序属于PnP驱动程序,同时它还支持电源管理协议。
WDM驱动程序 • 类驱动程序:为一类设备开发的驱动程序,负责完成设备所作的工作。 • 迷你驱动程序:如果你的设备在windows中存在类驱动程序,那么就需要一个迷你驱动程序。迷你驱动程序负责控制设备,和类驱动程序协同工作。 • 单功能WDM驱动程序:这种驱动程序控制设备所有的细节,不需要和其他设备驱动程序交互。 • WDM过滤驱动程序:通常这种驱动程序截获IO的请求来修改某个驱动程序的行为。
WDM驱动程序的工作机制(1/4) • 你可以把WDM驱动程序想象成一个例程的容器,操作系统会在适当的时机调用你的驱动程序的例程完成你的驱动程序想要完成的工作。作为一个WDM驱动程序的开发者,你的一项工作就是确定在这个容器中,放入多少例程。 • 和用户态应用程序类似,驱动程序也是一种可执行文件,文件扩展名为.sys。WDM驱动程序也有一个程序的入口,DriverEntry例程,类似于main()函数。和应用程序不同的是,驱动程序工作在系统进程的进程地址空间中,它只是提供一些例程供系统调用,而应用程序拥有自主权。
WDM驱动程序的工作机制(2/4) • DriverEntry 初始化驱动及其驱动对象 • AddDevice 初始化设备并创建设备对象 • Dispatch Routines 接收和处理IRPs • Unload 释放驱动获得的系统资源
WDM驱动程序的工作机制(3/4) • 下面简单解释一下系统可能调用哪些例程 • 用户将设备插入插槽中,系统将你的.sys文件映射到系统进程的地址空间,调用你的驱动程序的DriverEntry例程 • PnP管理器调用AddDevice例程,创建设备对象 • 应用程序打开设备的句柄,系统发送一些请求给你的派遣例程 • 应用程序试图向设备进行IO,IO管理器向你的派遣例程发送读写请求 • 用户拔掉设备,系统调用DriverUnload例程
WDM驱动程序的工作机制(4/4) • 和应用程序另一个不同的地方是驱动程序运行在任意线程上下文中,系统并不会为你的驱动程序代码产生一个单独的线程,系统会在需要的时候调用你的驱动程序例程,此时你不能确定是哪一个线程在调用你的例程。
分层驱动程序模型(1/4) • 对于WDM驱动程序,每个设备至少有两个驱动程序,一个称为功能驱动程序,另一个称为总线驱动程序。功能驱动程序了解设备的所有细节,负责处理IO和中断;总线驱动程序负责设备和计算机的连接,例如一个设备插入PCI总线插槽中后,PCI总线驱动程序负责分配给设备所需的资源。通常情况下,一个设备存在多个驱动程序,这些驱动程序被组合成堆栈的结构,这个堆栈成为设备堆栈
分层驱动程序模型(3/4) • PDO:物理设备对象,代表设备与总线的连接 • FDO:功能设备对象,功能驱动程序利用FDO 完成设备的功能 • FiDO:过滤设备对象,代表过滤设备
分层驱动程序模型(4/4) • IRP:IO请求包(IO Request Packet),IO管理器将所有与IO相关的上层用户IO请求封装成IRP这一个特殊的数据结构里,WDM驱动程序是包驱动的,通常IO管理器将IRP发送给设备堆栈上栈顶的驱动程序,然后每层驱动程序将IRP逐个向下传递。
DriverEntry例程 • DriverEntry例程是驱动程序的入口地址,系统在加载你的驱动程序的时候调用DriverEntry例程,DriverEntry例程主要是做一些初始化工作,填充驱动程序对象结构体的相关域,主要是各个例程的地址,包括DriverUnload例程,AddDevice例程,DriverStartIo例程,各个派遣例程。
AddDevice例程(1/2) • AddDevice例程创建代表你的设备的设备对象结构体,并填充该结构体的相关域。其中在设备对象结构体中有一个可以保留用户数据的DeviceExtension域,在AddDevice例程中通常开发人员将和设备相关的数据保留在这个结构中。创建好设备对象后AddDevice负责将设备对象关联到这个设备的设备堆栈上
中断优先级与线程优先级 • 绝大部分线程运行在IRQL 0或者IRQL 1上,所以不管线程的优先级是多少都不可以封锁硬件中断。线程调度是在IRQL 2上做出的,所以内核决定下一个将要运行的线程时,不会有线程被执行。 • CPU只可以被高中断优先级的中断请求所中断也就是说运行在IRQL 1上的线程可以中断运行在IRQL 0上的线程,而不管线程的优先级是多少。 • IRQL的重要性主要体现于运行在IRQL 2上的例程绝对不能产生页面错误,也就是说不能访问分页内存,因为系统无法进行调度。
IRP的数据结构(2/3) • MdlAddress是内存描述符的地址,IO方式有两种DO_DIRECT_IO和DO_BUFFERD_IO,当使用DO_DIRECT_IO时用这个域间接表示IO的数据在内存中的地址 • AssociateIrp是一个联合结构,当使用DO_BUFFERD_IO时,这个结构中包含进行IO时需要的参数 • IoStatus表示这个IRP被完成的情况 • PendingReturned表示下层驱动程序是否返回STATUS_PENDING
IRP的数据结构(3/3) • Cancel表示这个IRP是否被取消了 • CancelRoutinue表示当IRP被取消时调用的取消例程的地址 • Tail是一个复杂的结构,里面有一些关于设备队列的结构
IO堆栈的数据结构(1/4) • 当IO管理器或者驱动程序生成IRP的时候,还需要为这个IRP创建一个IO堆栈数组。通常这个设备堆栈有多少层就会创建多少个IO堆栈数组单元,有时候也会多创建一个。 • IO堆栈保存了相关IRP的许多重要参数,供驱动程序执行IO时使用。IO堆栈的使用为IRP复用技术提供了必要的条件。
IO堆栈的数据结构(3/4) • MajorFunction 这是一个整型变量,称为主功能代码,系统定义了一些列宏表示IRP的功能,例如IRP_MJ_READ表示这是个读操作 • MinorFunction 副功能代码,细化IRP的功能,同样,系统也定义了一些列宏 • Parameters 这是个联合,定义了一些参数信息,随主功能代码的不同而不同 • DeviceObject 表示这个IRP将要被传送的设备对象地址
IO堆栈的数据结构(4/4) • FileObject 表示和这个IRP相关联的文件对象的地址。例如对文件做IO时,这个域指向做IO的文件地址。 • CompletionRoutine IRP的完成例程的地址,当驱动程序完成IRP表示的操作后调用这个例程处理完成IRP后的操作 • Context 作为参数传入完成例程
处理IRP的标准模型(2/2) • IO管理器生成IRP • 将IRP发送给驱动程序的派遣例程,派遣例程或者完成IRP或者将IRP传递给下层驱动程序或者将IRP排队 • 调用StartIo例程,该例程负责控制设备进行IO,通常StartIo设置一些状态位然后连接一个中断服务例程 • ISR 当 IO操作完成时设备向CPU产生中断,系统调用你的中断服务程序,中断服务程序的最主要的任务是安排一个DPC例程 • DPC例程从设备队列中删除IRP然后返回IRP
编译和安装驱动程序 • 安装DDK,利用DDK工具编译驱动程序 • 编译、链接驱动程序需要sources文件和makefile文件 • 安装驱动程序需要inf文件或者直接修改注册表
同步IO与异步IO • 同步IO:调用IO函数的线程在发送完IO请求后阻塞本线程,直到这个IO请求被完成才唤醒本线程 • 异步IO:调用IO函数的线程在发送完IO请求后并不阻塞本线程,完成这个IO请求后通过一些同步机制本线程可以得到请求完成的通知
应用程序需要的工作 • 调用CreateFile打开设备的句柄 Handle = CreateFile("\\\\.\\IOCTL", GENERIC_READ │ GENERIC_WRITE, 0, NULL, OPEN_EXISTING, flags, NULL); • 调用DeviceIoControl向设备发送特定请求的IRP result = DeviceIoControl(Handle, Code, InputData, InputLength, OutputData, OutputLength, &Feedback, &Overlapped);
驱动程序需要的工作 • 处理IRP_MJ_DEVICE_CONTROL的IRP 为这样的IRP添加派遣例程 NTSTATUS DispatchControl(PDEVICE_OBJECT fdo, PIRP Irp) { PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength; ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength; ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; switch (code) { …… default: status = STATUS_INVALID_DEVICE_REQUEST; break; } return CompleteRequest(Irp, status, info); }
SOFTICE • SOFTICE是一种内核级别的调试器,功能十分强大,不仅可以对应用程序调试,还可以对驱动程序进行调试,除了可以在代码中设置断点外,还可以设置条件断点和内存断点等。同时SOFTICE还支持源代码级别和汇编代码级别的调试。 • SOFTICE的窗口界面
加载驱动程序符号表 • SOFTICE使用自己定义的符号表。SOFTICE的SymbolLoader可以很好的完成这个过程。启动SymbolLoader,设置驱动程序.sys文件的路径,然后点击Load和Translate选项,这样它就可以将.sys的符号信息转化为.nms符号文件并加载。可以选择SOFTICE的加载时机,这样用户就不用每次手动加载了。