缓冲策略

 
I/O 缓冲策略
很明显的,驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。这就就设计到设备的 I/O 缓冲策略。
读写请求的 I/O 缓冲策略
前面说到通过设置 Device 对象的 Flag 可以选择控制处理读写请求的 I/O 缓冲策略。下面对这些缓冲策略分别做一介绍。
1
、缓冲 I/O(DO_BUFFERED_IO)
在读写请求的一开始, I/O 管理器检查用户缓冲区的可访问性,然后分配与调用者的缓冲区一样大的非分页池,并把它的地址放在 IRP AssociatedIrp.SystemBuffer 域中。驱动程序就利用这个域来进行实际数据的传输。
对于 IRP_MJ_READ 读请求, I/O 管理器还把 IRP UserBuffer 域设置 成调用者缓冲区的用户空间地址。当请求完成时, I/O 管理器利用 这个地址将数据从驱动程序的系统空间拷贝回调用者的缓冲区。对 IRP_MJ_WRITE 写请求, UserBuffer 被设置为 NULL ,并把用户缓冲 区的数据拷贝到系统缓冲区中。
2
直接 I/O(DO_DIRECT_IO)
I/O
管理器首先检查用户缓冲区的可访问性,并在物理内存中锁定它。然后它为该缓冲区创建一个内存描述表 (MDL) ,并把 MDL 的地址 存放在 IRP MdlAddress 域中。 AssociatedIrp.SystemBuffer UserBuffer 都被设置为 NULL 。驱动程序可以调用函数 MmGetSystemAddressForMdl 得到用户缓冲区的系统空间地址,从而 进行数据操作。这个函数将调用者的缓冲区映射到非份页的地址空 间。驱动程序完成 I/O 请求后,系统自动从系统空间解除缓冲区的映射。
3
这两种方法都不是
这种情况比较少用,因为这需要驱动程序自己来处理缓冲问题。 I/O 管理器仅把调用者缓冲区的用户空间地址放到 IRP UserBuffer 域中。我们并不推荐这种方式。
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
管理器首先分配一个非分页池,它足够大地存放调用者的输入或输出缓冲区 ( 不管哪个更大 ) 。非分页缓冲区的地址放在 IRP AssociatedIrp.SystemBuffer 域中,然后把 IOCTL 的输入数据拷贝 到这个非份页缓冲区中,并把 IRP UserBuffer 域设置成调用者输出缓冲区的用户空间地址。当驱动程序完成 IOCTL 请求时, I/O 管理器将这个非份页缓冲区中的数据拷贝到调用者的输出缓冲区。注意这里同一个非份页池同时用于输入和输出缓冲区,因此驱动程序在向缓冲区写东西之前应该把输入的所有数据读出来。
2
直接输入缓冲输出 I/O(METHOD_IN_DIRECT)
I/O
管理器首先检查调用者输入缓冲区的可访问性,并在物理内存中将其锁定。然后为该输入缓冲区创建一个 MDL ,并把指定该 MDL 的指针存放到 IRP MdlAddress 域中。同时, I/O 管理器还在非份页池中分配一输出缓冲区,并把这个缓冲区的地址存放在 IRP AssociatedIrp.SystemBuffer 域中,并把 IRP UserBuffer 域设置成调用者输出缓冲区的用户空间地址。当驱动程序完成 IOCTL 请求时, I/O 管理器将非份页缓冲区中的数据拷贝到调用者的输出缓冲区。
3
缓冲输入直接输出 I/O(METHOD_OUT_DIRECT)
I/O
管理器首先检查调用者输出缓冲区的可访问性,并在物理内存中将其锁定。然后为该输出缓冲区创建一个 MDL ,并把指定该 MDL 的指针存放到 IRP MdlAddress 域中。同时, I/O 管理器还在非份页池中分配一输入缓冲区,并把这个缓冲区的地址存放在 IRP AssociatedIrp.SystemBuffer 域中, 同时把调用者用户输入缓冲区中的数据拷贝到系统缓冲区中,并把 IRP UserBuffer 域设置为 NULL
4
上面三种方法都不是 (METHOD_NEITHER)
I/O
管理器把调用者的输入缓冲区的地址放到 IRP 当前 I/O 堆栈单元的 Parameters.Devi ceIoControl.TypeInputBuffer 域中,把输出缓冲 区的地址存放到 IRP UserBuffer 域中。这两个地址都是用户空间地 址。
从上面的说明可以看出,在执行缓冲 I/O 时, I/O 管理器将在非份页池 中分配内存,如果调用者的缓冲区比较大时,分配的非份页池也将 比较大。非份页池是系统比较宝贵的资源,因此,如果调用者的缓 冲区比较大时,我们一般采用直接 I/O 的方式 ( 例如磁盘读写请求等 ) 这样不仅节省系统资源,另一方面由于省去了 I/O 管理器在系统缓冲 区和调用者缓冲区之间的数据拷贝,也提高了效率,这对存在大量 数据传送的驱动程序尤其明显。
可以注意到 DDK 中的 Samples 下,几乎所有的例程的读写请求都是直 I/O 的,而对于 IOCTL 请求则是缓冲区 I/O 的居多。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值