MSDN Kernel-Mode Driver Architecture学习笔记(1)——Introduction to Windows Drivers(3)

随学随记,暂时未经编程验证 Written by HOOK_TTG(Jamie Jiang)

 

4)         Multiprocessor-safe多处理器安全的:在配置多个处理器的平台也是安全稳定可靠的

基于NT技术的操作系统无论在单处理器还是对称多处理器(SMP)平台上运行的结果都是完全一致的,因此内核模式驱动程序也必须按照这个要求设计。

在任何一个微软Windows多处理器平台上,存在以下几个条件:

n  所有的CPU必须是相同的,并且这些处理器要么都有相同的协处理器,要么都没有协处理器。

n  所有的CPU分享内存并且具备相同的内存访问权。

n  在对称平台中,每个CPU都能访问内存、捕获中断和访问I/O控制寄存器。(相反,在非对称多处理器机器中。CPU必须为它自己的一组附属CPU捕获所有中断。)

为了在SMP平台上能安全运行,操作系统必须保证在某一个处理器上运行的代码不能同时访问和修改另一个处理器正在访问和修改的数据。例如,如果底层驱动程序的ISR正在一个处理器上处理某个设备中断,那么这个ISR就必须独占设备寄存器或者关键驱动定义的数据的访问权,以免其他处理器同时触发这个设备中断。

另外,驱动程序的I/O操作在单处理器机器中是串行化操作的,但是在多处理器机器中则可以重叠操作。换句话说,一个驱动程序的例程在一个处理器上处理到达的I/O请求的是同时,某个驱动程序例程可以在另一个处理器上处理设备通讯。不管内核模式驱动程序在单处理器还是多处理器机器上运行,都必须能与其他驱动程序例程同步访问任何驱动定义的数据或者系统提供的资源,以及同步访问物理设备。

NT内核组件提供了一个叫做自旋锁的同步机制。在一个对称多处理器平台中,驱动程序使用自旋锁与其他例程并行存取,达到保护共享的数据(或者设备寄存器)的目的。内核对自旋锁的使用有两个策略:

n  只能有一个例程拥有某一特定时刻的自旋锁。在访问共享数据之前,每个引用这个数据的例程必须首先获取这个数据的自旋锁。为了访问这个数据,其他例程就必须获取这个自旋锁,但是必须等到当前拥有这个自旋锁的例程释放掉自旋锁才行。

n  内核为系统中的每个自旋锁分配一个IRQL值。每个内核模式例程只能获取与它所分配IRQL值相符的自旋锁。

这些策略阻止了一个运行在低IRQL的驱动程序例程抢占其他具有高优先级驱动程序所拥有的自旋锁。这就避免了死锁的发生。

拥有更高级IRQL值的例程可以获取拥有比它IRQL级别低的自旋锁。

例如,底层驱动程序的ISR会频繁共享驱动程序DPC例程的状态域。DPC例程调用驱动程序支持的临界区例程来访问这个共享域。保护这个共享区的自旋锁将拥有与这个设备中断的DIRQL相同的值。一旦临界区例程拥有了这个自旋锁,将在这个DIRQL优先级别上访问共享数据,那么这个ISR就不会在另外的单处理器或者SMP机器上运行了。

n  ISR不能在单处理器上运行是因为这个设备中断被屏蔽了,参看MSDNAlways Preemptible and Always Interruptible章节。

n  SMP机器中,在临界区例程获取了这个DIRQL级别的自旋锁并访问共享数据,那么ISR将不能再获得这个保护数据的自旋锁。

一组内核模式线程可以通过等待内核的调度对象来达到同步访问共享数据或者资源的目的,这些调度对象有:an event(事件)mutex(互斥体)semaphore(信号量)timer(定时器)或者其他线程。但是大多数驱动程序不会创建自己的线程,因为避免线程上下文的切换可以获得更好的性能。对于对时间要求严格的内核模式支持例程和驱动程序将运行在IRQL=DISPATCH_LEVEL或者DIRQL,而且他们必须使用内核的自旋锁来同步访问共享数据或者资源。

更多的信息,参看 MSDN Spin Locks, Managing Hardware Priorities, and Kernel Dispatcher Objects等章节。

5)         Object-based:面向对象的

基于NT技术的操作系统是面向对象。执行程序中的各种组件都定义成了一个或多个对象类型。每个组件提供的内核模式支持例程就是操作它的对象类型实例。没有组件可以直接访问另一个组件的对象。必须通过调用组件的支持例程才能使用组件的对象。

         这种设计使得操作系统同时具有可移植性和灵活性。例如,将来操作系统可能含有一个重新编码的内核组件,这个组件是具有相同对象类型定义的未来版本,但是组件的内部结构却可能完全不同。如果假设这个重新编码版本的内核提供了与当前版本一致的支持例程,具有相同的名字和参数,那么对当前系统中执行组件的移植将没有任何影响。

同样的,为了保持可移植性和可配置性,驱动程序必须仅使用WDK中描述的支持例程和接口来获得与操作系统的相通和支持。

和操作系统一样,驱动程序也是面向对象。例如:

n  File objects(文件对象)代表用户模式应用程序对设备的连接。

n  Device objects(设备对象)代表每个驱动对象的逻辑、虚拟或者物理设备。

n  Driver objects(驱动程序对象)代表每个驱动对象的载入镜像。

I/O管理器为文件对象、设备对象和驱动程序对象定义了结构和接口。

就像其他执行组件一样,驱动程序要使用对象也需要通过调用I/O管理器和其他系统组件提供的内核模式支持例程。内核模式例程都有便于识别特定对象的名字,这个名字包含了例程执行的操作以及例程对这个对象执行的操作。

这些支持例程的名字具有以下格式:

PrefixOperationObject

Prefix:指示内核模式组件提供的支持例程,通常是这个组件定义的对象类型。大多数前缀有两个字符。

Operation:描述这个对象是用来做什么的。

Object:标识对象的类型。

例如,I/O管理器的IoCreateDevice例程用来创建一个代表物理、逻辑或者虚拟的设备,这个设备用于处理I/O请求。

一个系统组件提供的例程可以调用其它组件提供的例程。这样可以减少驱动程序必须创建的调用数目。特别是I/O管理器,提供了某些便于驱动程序开发的例程。例如,IoConnectInterrupt就是内核为中断对象提供的支持例程,由底层驱动程序调用来注册他们的ISR

                   对象的不透明性

一些系统定义的对象是不透明的:只有defining system component(定义系统组件)知道这个对象的内部结构,并且可以直接访问这个对象包含的所有的数据。定义了不透明对象系统组件会提供一个支持例程方便驱动程序和其他内核模式组件调用来操作这个对象。驱动程序从不直接访问不透明对象的结构。

注意:为了保持驱动程序的可移植性,必须使用系统提供的支持例程来操作系统定义的对象。只有定义系统组件可以在任意时刻改变对象类型的内部结构。

6)         Packet-driven I/O with reusable IRPs 使用可重用IRPs模式的包驱动I/O

I/O管理器、即插即用管理器和电源管理器使用I/O request packets(I/O请求包,缩写 IRPs)与内核模式驱动程序通讯,并且也允许驱动程序使用IRPs互相通讯。

I/O管理器执行过程有下列几步:

n  接受I/O请求,这些请求来自用户模式应用程序

n  创建描述这个I/O请求的IRPs

n  将这个IRPs路由到合适的驱动程序

n  跟踪这个IRPs,直到IRPs被处理完成

n  向每个I/O操作请求返回状态

一个IRP能被路由到多个驱动程序。例如,一个要在磁盘上打开文件的请求可能首先被发送到文件系统驱动程序,通过一个中间镜像驱动,最后才到达磁盘驱动程序,也可能到一个PnP硬件总线驱动程序。这个驱动程序集合称之为 drvier stack(驱动程序栈)

因此,每个控制设备的驱动程序的IRP都有一个固定部分,外加一个驱动程序特定的I/O栈存储单元:

n  在固定部分中(或者头部),有I/O管理器负责维护的原始请求信息,比如调用的线程ID和参数、有文件打开的设备对象地址,等等。固定部分还包含了一个I/O状态块,用于驱动程序设置I/O操作请求的状态信息。

n  在最高层驱动程序的I/O栈存储单元中,I/O管理器、即插即用管理器或者电源管理器会设置驱动程序特定的参数,比如请求操作的功能代码和让驱动程序决定如何操作的上下文。每个驱动程序需要在驱动程序栈中为下一层的驱动程序创建I/O栈单元。

由于每个驱动程序处理一个IRP,并且它可以访问其在IRP中的I/O栈存储单元,所以在驱动程序操作的每个阶段可以重用IRP。另外,高层驱动程序可以创建(或者重利用)IRPs,并将其发送到低一层的驱动程序。

更多讨论IRPs的信息,参看 MSDN中的 Handling IRPs章节。

7)         支持异步I/O操作

I/O管理器提供了对异步I/O操作的支持,因此I/O请求的发起者(通常是一个用户模式应用程序,有时可能是另一个驱动程序)在发出请求时可以继续执行其它操作,不用一直等到这个请求被处理完毕。异步I/O的支持不仅提高了整个系统的吞吐量还改善了发出I/O请求的代码性能。

         在异步I/O的支持下,内核模式驱动没必要按照那些I/O请求被发送到I/O管理器的顺序处理这些请求。I/O管理器或者更高一级的驱动程序可以对接收的I/O请求重新排序。驱动程序还可以将一个大的数据传输请求分割成很多小的传输请求。此外,特别是在对称多处理器平台下,驱动程序还可以重叠处理I/O请求, 就像 Multiprocessor-Safe(多处理器安全)章节中讨论那样。

         另外,内核模式驱动程序对一些个别的I/O请求是不需要串行化的。换句话说,驱动程序没必要一定要将每个IRP处理完成才能接受下一个传入的I/O请求。

         每当一个驱动程序接收到IRP,它都会响应并做出特定的IRP处理。如果一个驱动程序支持异步IRP处理,它将向下一个驱动程序发送IRP;如果有必要,不用等待第一个IRP被处理完毕就可以开始处理下一个IRP。驱动程序可以注册一个“Completion routine(处理完毕例程)”,I/O管理器将在另一个驱动程序处理完一个IRP的时候调用这个例程。驱动程序为IRPI/O状态块提供了一个状态值,其他驱动程序可以访问这个状态值来确定I/O请求的状态。

         驱动程序可以维护关于当前I/O操作的状态信息,这个状态信保存在它们的设备对象中称之为 device extension(设备扩展)的部位。

更多信息,参看MSDN中的 Handling IRPs and Input/Output Techniques章节。

4、            内核模式驱动的例子

 

WDK提供了各种各样的内核模式驱动例子。在安装WDK后,在src/general子目录下包含了适用于所有内核模式驱动的驱动程序示例代码。

这些代码下面这些:

Toaster

         提供了一套符合WDM规范的示例代码。这些示例还包含有安装软件样例。

ioctl

         演示了驱动程序如何支持I/O操作的代码。

event

         演示了如果应用程序请求一个通知,内核模式驱动如何将这个应用程序请求通知给硬件事件。一个例子使用 event objects(事件对象),另一个例子使用通知请求排队的方法。

cancel

         演示了cancel-safe IRP queues的使用。

tracedrv

         演示如何使用 WPP software tracing

/src目录中的其他子目录包含了用于其他各种硬件类型的示例代码。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值