Chinaunix首页 | 论坛 | 博客
  • 博客访问: 591419
  • 博文数量: 126
  • 博客积分: 4379
  • 博客等级: 上校
  • 技术积分: 2110
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-06 22:35
文章分类

全部博文(126)

文章存档

2012年(5)

2011年(3)

2010年(2)

2009年(116)

分类: LINUX

2009-03-26 20:57:33

NT还有更多其他的对象,例如中断对象、Controller对象、定时器对象等等,但在我们开发的驱动程序中并没有用到,因此在这里不做介绍。
I/O
缓冲策略

很明显的,驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。这就就设计到设备的I/O缓冲策略。

读写请求的I/O缓冲策略

前面说到通过设置Device对象的Flag可以选择控制处理读写请求的I/O缓冲策略。下面对这些缓冲策略分别做一介绍。
1
、缓冲
I/O(DO_BUFFERED_IO)
在读写请求的一开始,I/O管理器检查用户缓冲区的可访问性,然后分配与调用者的缓冲区一样大的非分页池,并把它的地址放在IRPAssociatedIrp.SystemBuffer域中。驱动程序就利用这个域来进行实际数据的传输。

对于IRP_MJ_READ读请求,I/O管理器还把IRPUserBuffer域设置 成调用者缓冲区的用户空间地址。当请求完成时,I/O管理器利用 这个地址将数据从驱动程序的系统空间拷贝回调用者的缓冲区。对 于IRP_MJ_WRITE写请求,UserBuffer被设置为NULL,并把用户缓冲 区的数据拷贝到系统缓冲区中。

2
、 直接
I/O(DO_DIRECT_IO)
I/O
管理器首先检查用户缓冲区的可访问性,并在物理内存中锁定它。然后它为该缓冲区创建一个内存描述表(MDL),并把MDL的地址 存放在IRPMdlAddress域中。AssociatedIrp.SystemBuffer UserBuffer都被设置为NULL。驱动程序可以调用函数 MmGetSystemAddressForMdl得到用户缓冲区的系统空间地址,从而进行数据操作。这个函数将调用者的缓冲区映射到非份页的地址空 间。驱动程序完成I/O请求后,系统自动从系统空间解除缓冲区的映射。

3
、 这两种方法都不是

这种情况比较少用,因为这需要驱动程序自己来处理缓冲问题。 I/O管理器仅把调用者缓冲区的用户空间地址放到IRPUserBuffer 域中。我们并不推荐这种方式。


IOCTL
缓冲区的缓冲策略

IOCTL请求涉及来自调用者的输入缓冲区和返回到调用者的输出缓冲区。为了理解IOCTL请求,我们先来看看WIN32 API DeviceIoControl函数的原型。
BOOL DeviceIoControl (
HANDLE hDevice, //
设备句柄

DWORD dwIoControlCode, // IOCTL
请求操作代码

LPVOID lpInBuffer, //
输入缓冲区地址

DWORD nInBufferSize, //
输入缓冲区大小

LPVOID lpOutBuffer, //
输出缓冲区地址

DWORD nOutBufferSize, //
输出缓冲区大小

LPDWORD lpBytesReturned, //
存放返回字节数的指针

LPOVERLAPPED lpOverlapped //
用于同步操作的Overlapped结构体指针

);
IOCTL
请求有四种缓冲策略,下面一一介绍。

1
、 输入输出缓冲
I/O(METHOD_BUFFERED)
I/O
管理器首先分配一个非分页池,它足够大地存放调用者的输入或输出缓冲区(不管哪个更大)。非分页缓冲区的地址放在IRPAssociatedIrp.SystemBuffer域中,然后把IOCTL的输入数据拷贝 到这个非份页缓冲区中,并把IRPUserBuffer域设置成调用者输出缓冲区的用户空间地址。当驱动程序完成IOCTL请求时,I/O管理器将这个非份页缓冲区中的数据拷贝到调用者的输出缓冲区。注意这里同一个非份页池同时用于输入和输出缓冲区,因此驱动程序在向缓冲区写东西之前应该把输入的所有数据读出来。

2
、 直接输入缓冲输出
I/O(METHOD_IN_DIRECT)
I/O
管理器首先检查调用者输入缓冲区的可访问性,并在物理内存中将其锁定。然后为该输入缓冲区创建一个MDL,并把指定该MDL的指针存放到IRPMdlAddress域中。同时,I/O管理器还在非份页池中分配一输出缓冲区,并把这个缓冲区的地址存放在IRPAssociatedIrp.SystemBuffer域中,并把IRPUserBuffer域设置成调用者输出缓冲区的用户空间地址。当驱动程序完成IOCTL请求时,I/O管理器将非份页缓冲区中的数据拷贝到调用者的输出缓冲区。

3
、 缓冲输入直接输出
I/O(METHOD_OUT_DIRECT)
I/O
管理器首先检查调用者输出缓冲区的可访问性,并在物理内存中将其锁定。然后为该输出缓冲区创建一个MDL,并把指定该MDL的指针存放到IRPMdlAddress域中。同时,I/O管理器还在非份页池中分配一输入缓冲区,并把这个缓冲区的地址存放在IRPAssociatedIrp.SystemBuffer域中, 同时把调用者用户输入缓冲区中的数据拷贝到系统缓冲区中,并把IRP UserBuffer域设置为NULL

4
、 上面三种方法都不是
(METHOD_NEITHER)
I/O
管理器把调用者的输入缓冲区的地址放到IRP当前I/O堆栈单元的Parameters.Devi ceIoControl.TypeInputBuffer域中,把输出缓冲 区的地址存放到IRPUserBuffer域中。这两个地址都是用户空间地 址。

从上面的说明可以看出,在执行缓冲I/O时,I/O管理器将在非份页池 中分配内存,如果调用者的缓冲区比较大时,分配的非份页池也将 比较大。非份页池是系统比较宝贵的资源,因此,如果调用者的缓 冲区比较大时,我们一般采用直接I/O的方式(例如磁盘读写请求等) 这样不仅节省系统资源,另一方面由于省去了I/O管理器在系统缓冲 区和调用者缓冲区之间的数据拷贝,也提高了效率,这对存在大量 数据传送的驱动程序尤其明显。
可以注意到DDK中的Samples下,几乎所有的例程的读写请求都是直 I/O的,而对于IOCTL请求则是缓冲区I/O的居多
阅读(787) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~