使用DMA的设备直接在用户缓冲区上进行IO操作:
上图说明了驱动怎样使用IRP中的MdlAddress来传输数据,此操作为读数据过程。上图中的驱动使用基于包的传输,bus-master模式或system模式都可以。
1、用户空间虚拟地址的某一范围,代表当前线程的缓冲区。缓冲区(虚拟地址空间)的内容实际上可能存储于一些物理上离散的页面中(如上图中的阴影部分)。IO管理器创建一个MDL来描述缓冲区。MDL是一个不透明的数据结构,由内存管理器定义。MDL用于将某一特殊的虚拟地址范围映射到一个或多个基于页的物理地址范围。
2、IO管理器为当前线程的读请求服务。线程传入一个用户空间地址范围,此地址范围代表了一个缓冲区。
3、IO管理器或FSD(File System Dispatch)检查用户适用的缓冲区是否可以访问,并调用MmProbeAndLockPages,MmProbeAndLockPages适用之前创建的MDL作为参数。MmProbeAndLockPages同时填满MDL中相应的物理内存。
如上图所示,表示虚拟地址的MDL可能有几个基于页的物理地址入口。缓冲区的虚拟空间范围可能起始和结束于某些字节偏移,例如,从MDL描述的页面中的第一页起始部分和最后一页起始部分。
4、IO管理器提供了一个MDL的指针,该指针存储于发起传输操作请求的IRP中。在驱动完成IRP的请求,IO管理器或文件系统调用MmUnlockPages函数之前,MDL中描述的物理页一直是锁定的,并且分配给了缓冲区。MDL中的虚拟地址能变成不可见的,甚至在IRP被送到设备驱动(或位于设备驱动上层的中间驱动)之前。
5、如驱动使用基于包的system或bus-master DMA,它的AdapterControl例程调用MmGetMdlVirtualAddress。MmGetMdlVirtualAddress使用IRP中的MdlAddress指针作为参数,以获取MDL的基于页的入口的虚拟地址基址。
6、AdapterControl例程调用MapTransfer,直接从设备中读取数据到物理内存。MapTransfer使用MmGetMdlVirtualAddress返回的基地址作为参数。
驱动必须时常检查缓冲区的长度。IO管理器并不为长度为0的缓冲区创建MDL。
(本文议自于MSDN,不明之处请参考原文Using Direct I/O with DMA)