windows驱动程序框架

上传人:子 文档编号:43331472 上传时间:2018-06-05 格式:DOC 页数:29 大小:58KB
返回 下载 相关 举报
windows驱动程序框架_第1页
第1页 / 共29页
windows驱动程序框架_第2页
第2页 / 共29页
windows驱动程序框架_第3页
第3页 / 共29页
windows驱动程序框架_第4页
第4页 / 共29页
windows驱动程序框架_第5页
第5页 / 共29页
点击查看更多>>
资源描述

《windows驱动程序框架》由会员分享,可在线阅读,更多相关《windows驱动程序框架(29页珍藏版)》请在金锄头文库上搜索。

1、windowswindows 驱动程序框架驱动程序框架windows 驱动程序框架 2007-06-14 18:50:49| 分类: 好的技术 | 标签:我的技术 |字号大中小 订阅 .windows 驱动程序框架 2006-11-23 17:15:00 | By: 赛伯 一、驱动程序框架介绍很多人都用过 VC+等图形集成开发环境(IDE)开发过 Windows应用程序,当用集成开发环境生成一个工程时,会自动生成一个预先定义好的命令行,这个命令行包含了编译器(compiler)和连接器(linker)某些缺省的配置。很多习惯于图形集成开发环境的人可能对此并不了解。你可能用 IDE 生成过 GU

2、I 应用程序,也可能生成过 console 应用程序,这是两种不同的子系统(subsystem)应用程序,如果你注意观察,可能会发现,console 应用程序中是以main 函数为入口函数,而 GUI 应用程序是以 WinMain 函数为入口函数。在工程设置上,console 应用程序的设置是/SUBSYSTEM:CONSOLE,而 GUI 应用程序的设置是/SUBSYSTEM:WINDOWS。而驱动程序的设置是/SUBSYSTEM:NATIVE。虽然在工程设置里,你可以通过选项“-entry:DriverEntry”来设定驱动程序的入口函数名字,但驱动程序的入口函数一般都命名为 Driver

3、Entry,DriverEntry 已经成为官方缺省的驱动函数入口名称。连接器(linker)根据 windows 可执行文件 PE 头的设置,生成最后的二进制文件,PE 文件头的设置还决定了这个可执行文件如何被加载的,例如是作为一个可执行文件被加载,还是作为一个动态链接库被加载,还是作为一个驱动程序被加载。加载器(loader)会根据这些设置来验证是否支持所设定的加载模式。我们只需设置好加载模式,加载器就会根据这个设置来加载我们的程序。一般的驱动程序设置如下:/SUBSYSTEM:NATIVE /DRIVER:WDM -entry:DriverEntry在开始写 DriverEntry 之前

4、,我们先说一下驱动程序的一些特殊之处。我知道,很多人都想能够尽快写一个驱动程序,想看看到底驱动程序是如何工作的。这在写 windows 应用程序时,经常是这样的,拿一个例子来,改动一下,编译通过后,运行测试。如果运行不正确,应用程序崩溃了,或者消失了,这对系统不会造成多大影响,但是在编写驱动程序时,出现错误会导致蓝屏,当面对蓝屏时,往往会不知所措,如果驱动程序是在系统启动时加载的,情况会更糟糕。这时只有重新启动系统,进入到安全模式,恢复到先前的硬件配置。首先应该知道的是,驱动程序是加载到系统内核中的,如果驱动程序编写不当,会影响到系统的完整性,驱动程序中的 BUG 可能会导致整个系统的崩溃。W

5、indows 采用虚拟内存机制,系统会将内存中某些页面交换到外部磁盘上来,这对应用程序是透明的,影响不大,但是有时候驱动程序要求访问的内存是不能被交换到外部磁盘的,必须在内存中,否则可能会引起系统蓝屏。驱动程序中使用内存必须小心,在某些情况下,如果一个驱动占用了可交换的内存页面,系统会尽可能的将这些页面保持在内存中。如果关闭了应用程序,驱动仍旧占用内存,这 bug 是很难发现的,除非进行驱动验证(driver verify) 。 (需深入理解)关于 IRQL 和 IRP,微软的 MSDN 有很长的篇幅来描述,这里只是尽可能用比较简单的描述来解释它。IRQL(Inerrupt Request L

6、evel) ,中断请求级别,任何一个进程都是在线程中执行的,而任何一个线程都运行于一定的 IRQL,进程的 IRQL 决定了线程允许如何被中断。同一个处理器上线程只能被具有更高 IRQL 级别的线程所中断,低优先级或同等优先级的中断会被屏蔽,只有高级别的 IRQL 才会中断。在多处理器系统中,每个处理器都有自己独立的 IRQL。系统共有四种级别的 IRQL,分别是“Passive” “APC”“Dispatch” “DIRL” 。IRQL 级别越高,可调用的 API 函数就越少。MSDN 的内核函数 API 文档中都会注明在哪个中断请求级别上调用。例如 DriverEntry 函数就是运行在

7、PASSIVE_LEVEL。PASSIVE_LEVEL 是最低级的 IRQL,不会屏蔽任何中断。用户态应用程序的线程就运行在这个级别上,可以使用可交换的内存。APC_LEVEL,异步调用就运行在这个级别,这时会屏蔽 APC 级别的中断。在这个级别仍可访问可交换内存。当一个 APC 中断发生时,处理器提升到 APC 级别,这时,就禁止了其他的 APC 中断。驱动程序自己提升到 APC 级别,以便处理同步操作。DISPATCH_LEVEL,运行于这个级别的处理器会屏蔽除 DPC 以外的中断,不能访问可交换内存,所以这个级别能调用的 API 函数大大减少。DIRQ,设备级中断,这是硬件设备的中断,一

8、般高层的驱动程序不需要处理这个中断,只有底层的驱动程序才处理这个中断。刚开始学习编写驱动程序,可以先集中精力学习驱动程序的框架,但是,对中断级别要有一定的理解。IRP(I/O Request Packet) ,中断请求包,它会沿着驱动程序栈在驱动程序间传递。IRP 包是由 I/O 管理器或者是另一个驱动程序产生,并传递到你的驱动程序中来,驱动程序利用 IRP 包来传递信息并完成请求任务。IRP 包中包含所请求的操作信息。IRP 包在 MSDN 中的解释很详细,大约有二十多页。IRP 包包含一个“子请求”的列表,也称为 IRP 栈。为了形象的解释 IRP 栈是如何工作的,我们做一个比喻。假设你有

9、三个人,一个是木匠,一个是管钳工,一个是焊工,他们三个共同建筑一个房子,他们有自己的工具箱,每个人都要完成自己的工作。而 IRP 包就是发起建造房子的总命令。一旦建造房子的 IRP 总命令下达以后,每个人开始自己的工作,每个人都完成自己的工作以后,建造房子的总命令也就完成了。现在我们开始讨论 DriverEntry 例程,声明如下:NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath);参数 DRIVER_OBJECT 是一个数据结构,表示这个驱动。RegistryPath 是一个字符串

10、,在注册表中描述这个驱动的一些信息,驱动程序也可以在注册表的这个位置添加一些特殊的信息。在这个例程中,一般要用到一个有用的数据结构,那就是DEVICE_OBJECT,它表示一个特定的设备,一个驱动程序有可能操纵多个设备,可以用这个数据结构来区分不同的设备。下面我们来编写 DriverEntry 例程,第一件事情就是创建一个设备,也许你会感到困惑,没有实际的物理设备,我们如何创建设备,虽然驱动程序往往是和具体的硬件联系在一起,但是驱动程序也可以不和特定的硬件设备相绑定。驱动程序也有很多类型,驱动程序也分不同的层次,并不是所有的驱动程序都和硬件打交道。最高层的驱动程序一般要和用户层的应用程序相交互

11、,最底层的驱动程序一般和具体硬件或者其他驱动打交道。系统中有网卡驱动,显卡驱动,文件驱动等等,每种驱动都有自己的驱动栈。驱动栈或者把一个 IRP 请求分成几个请求传给其他驱动栈,或者只是简单的把这个请求转发给底层的驱动。我们以磁盘操作为例,根用户态应用程序交互的驱动程序并不直接和底层的硬件打交道,高层的驱动只是管理文件系统本身,当要进行读写时,它会和位于它下面的中间层驱动交互,中间层驱动和底层的驱动交互,底层的驱动才进行实际的物理操作。下面分析一下 DriverEntry 的前一部分NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNIC

12、ODE_STRING pRegistryPath)NTSTATUS NtStatus = STATUS_SUCCESS;UINT uiIndex = 0;PDEVICE_OBJECT pDeviceObject = NULL;UNICODE_STRING usDriverName, usDosDeviceName;DbgPrint(“DriverEntry Called rn“);RtlInitUnicodeString(RtlInitUnicodeString(NtStatus = IoCreateDevice(pDriverObject, 0,第一个函数时 DbgPrint 函数,这相当于

13、应用层的 printf 函数,它会将调试信息发送给调试器,你可以用软件“DBGVIEW”来查看打印信息,这个软件可以在 下载。第二个函数是 RtlInitUnicodString,这个函数初始化一个UNICODE_STRING 数据结构,这个数据结构包含三个域,第一个域是这个 UNICODE 字符串的长度,第二个域是 UNICODE 字符串最大长度,第三个域是一个指向这个字符串的指针。在驱动程序中很多地方都会使用这个 UNICODE 字符串结构,记住,这个字符串结构不是以NULL 结尾的,因为它有字符长度这个信息,所以无需以 NULL 结尾。新手有时会以为这种字符串是以 NULL 结尾的,结

14、果往往会造成蓝屏。设备有自己的名字,设备的命名往往如下所示:Device,这个名字是调用 IoCreateDevice 时的参数。IoCreateDevice 函数的第一个参数一个指向设备的指针,第二个参数是我们设置为 0,这个参数的意思是指设备扩展数据结构的大小,可以通过这个数据结构传递驱动的所需的信息,这个参数比较重要,在这个例子中我们没有用到,所以暂时设置为 0。现在我们已经成功的创建了DeviceExample 设备驱动,现在需要设置相应 IRP 包的函数指针。for(uiIndex = 0; uiIndex MajoruiIndex = Example_UnSupported;pDr

15、iverObject-MajorIRP_MJ_CLOSE = Example_Close;pDriverObject-MajorIRP_MJ_CREATE = Example_Create;pDriverObject-MajorIRP_MJ_DEVICE_CONTROL = Example_IoControl;pDriverObject-MajorIRP_MJ_READ = Example_Read;pDriverObject-MajorIRP_MJ_WRITE = USE_WRITE_;我们设置好 Create,Close,IoControl,Read,Write 等函数指针,当应用层程序调

16、用一定的 API 函数时,驱动程序就会调用这些设置好的函数。IRP 包和 API 函数的对应关系如下,CreateFile - IRP_MJ_CREATECloseHandle - IRP_MJ_CLEANUP 如果想动态的卸载驱动,必须设置这个函数指针,如果不指定这个函数指针那么你的驱动一旦被装载,系统就不会卸载掉它。下面的代码使用的是 DEVICE_OBJECT,不是 DRIVER_OBJECT,这两个数据结构可能有些相似,容易引起混扰,但是它们代表不同的对象。pDeviceObject-Flags |= IO_TYPE;pDeviceObject-Flags 这里设置设备标志,IO_TYPE 这个标志在后面详细描述。DO_DEVICE_INITIALIZING 告诉 I/O 管理器,设备正在初始化,不要发送 I/O 请求包给这个驱动。在 DriverEntry 函数中,这个设置并不需要,因为 I/O 管理器会自动设置这个标志,并且退出DriverEntry 时,I/O

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 生活休闲 > 科普知识

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号