1、通常,驱动程序在某些不可预测线程的上下文中应该使用异步方式处理I/O请求。我们使用术语任意线程上下文(arbitrary thread context)来描述驱动程序并不知道(或并不关心)处理器当前执行在哪一个线程上的上下文。
2、 Windows 2000使用对称多处理器模型,即所有的处理器都是相同的,系统任务和用户模式程序可以执行在任何一个处理器上,并且所有处理器都平等地访问内存。多处理器的存在给设备驱动程序带来了一个困难的同步问题,因为执行在多个CPU上的代码可能同时访问共享数据或共享硬件资源。Windows 2000提供了一个同步对象,自旋锁(spin lock),驱动程序可以使用它来解决多处理器的同步问题。
3、当CPU运行在 PASSIVE_LEVEL级时,当前运行的线程可以被任何优先级大于它的线程抢先。然而,一旦CPU的IRQL大于PASSIVE_LEVEL级,线程抢先将不再发生,此时CPU执行在使CPU越过PASSIVE_LEVEL级的任意线程上下文中。但是,运行在任何IRQL级上的活动都可以被更高 IRQL级上的活动中断。所以驱动程序必须假定在任何时刻都可能失去控制权,而此时系统可能需要执行更基本的任务。
4、为了实现可配置性,首先应该在代码中避免直接引用硬件,即使是在平台相关的条件编译块中也是这样。应该使用HAL工具或调用低级总线驱动程序,或者实现一个标准的或定制的控制接口,并通过控制面板程序与用户交互。另外,还应该支持Windows管理仪器(WMI)控件,这种控件允许用户和管理员在分布式企业环境中配置硬件特征。最后,应该使用注册表作为配置信息的数据库,这可以使配置信息在系统重新启动后仍然存在。
5、为了实现这种可移植性,驱动程序应该全部用C写,并且只使用ANSI C标准规定的语言元素。应避免使用编译器厂商专有的语言特征,并避免使用没有被操作系统内核输出的运行时间库函数(参见第三章)。如果不能避免驱动程序中的平台依赖,至少应该用条件编译指令隔离这些代码。
6、可以把一个完整的驱动程序看作是一个容器,它包含许多例程,当操作系统遇到一个IRP时,它就调用这个容器中的例程来执行该IRP的各种操作。
可以把一个完整的驱动程序看作是一个容器,它包含许多例程,当操作系统遇到一个IRP时,它就调用这个容器中的例程来执行该IRP的各种操作。有些例程,例如DriverEntry和AddDevice,还有与几种IRP对应的派遣函数将出现在每一个这样的容器中。需要对IRP排队的驱动程序一般都有一个 StartIo例程。执行DMA传输的驱动程序应有一个AdapterControl例程。大部分能生成硬件中断的设备,其驱动程序都有一个中断服务例程 (ISR)和一个推迟过程调用(DPC)例程。驱动程序一般都有几个支持不同类型IRP的派遣函数,其中三个派遣函数是必须的。所以,WDM驱动程序开发 者的一个任务就是为这个容器选择所需要的例程。
图示 P21 1-5 WDM驱动程序“容器”中的内容
7、有三种注册表键负责配置。它们是硬件(hardware)键、类 (class)键、服务(service)键。必须明确一点,这些名字(指hardware、class、service)并不是某个专用子键的名称:它 们是这三种键的一般称谓,其具体的路径名要取决于它们所属的设备。概括地讲,硬件键包含单个设备的信息,类键涉及所有相同类型设备的共同信息,服务键包含 驱动程序信息。有时人们用“实例(instance)键”和“软件(software)键”来代表硬件键和服务键。
设备的硬件键出现在注册表local machine分支的\System\CurrentControlSet\Enum子键上。
应用程序经常需要访问注册表中关于硬件设备的信息。为了使这成为可能而同时又不暴露重要的Enum键,Microsoft提供了一组SetupDiXxx函数。
设备接口的符号连接名(通过枚举该接口GUID的所有实例或者从WM_DEVICECHANGE消息的参数中获得这个名字)。ClassGUID是设备类GUID(全局唯一标识符)的ASCII形式;在效果上,它是一个指向该设备类键的指针。Service指向服务键。
类键 所有设备类的类键都出现在HKLM\System\CurrentControlSet\Control\Class键中。它们的键名是由Microsoft赋予的GUID值。
服务(或软件)键 对设备驱动程序重要的最后一个键是服务键。它指出驱动程序执行文件的位置,以及控制驱动程序装入的一些参数。服务键位于HKLM\System\CurrentControlSet\Services键中。
当我说系统“装入”一个驱动程序时,是指系统把驱动程序的映像映射到虚拟内存中,并重定位内存参考,最后调用驱动程序的主入口点。主入口点通常命名为 DriverEntry。我将在本章后面讲述DriverEntry函数。如果驱动程序已经在内存中,则装入过程仅仅是增加驱动程序映像的参考计数。
8、驱动程序的装入顺序并不是很重要。系统以PnP管理器装入驱动程序的顺序调用驱动程序的AddDevice函数,而这个顺序与设备对象在设备堆栈中出现的顺序完全一致。
图示2-2 设备递归枚举过程
图示2-2显示了一个由多个设备堆栈组成的树形结构,但这并不表示IRP必须从上一层的PDO流向下一层的FiDO。实际上,一个堆栈的PDO驱动程序就是其下一层堆栈的FDO驱动程序,见图示2-2中的阴影块。当驱动程序以PDO角色接收到一个IRP时,它对该IRP执行一些操作但不发出这个或其它IRP到设备(以FDO角色)。相反,当总线驱动程序以FDO角色接收到一个IRP时,它可能需要向设备发送某些IRP(以PDO角色)。
图示 图2-8 设备(位于二级总线)读请求处理流程