zz: 设备读写 之 直接方式(Direct I/O)MmGetMdlByteCount/MmGetMdlByteOffset/MmGetMdlVirtualAddress

最近一直在看WDM的驱动,在memory方面有一些不是很清楚,譬如 MDL 的具体结构、使用方法等等。

在网络上搜刮一番,了解了MDL的具体使用,主要是MmGetMdlByteOffset这个宏的意义。之前一直认为这个值应该是0。事实并非如此,这个值是一个相对于page start addrss的offset,也就是说Va转为Pa时,并没有从page start address开始,而是从其中的某个offset开始。至于为什么会这样,尚未了解。但是对于这样的一种情况,使用方法会有一些区别。譬如在错DMA传输时,MDL需要重新build。


ZZ:http://www.cnblogs.com/lzjsky/archive/2010/11/19/1881926.html

这种方式需要在创建完设备对象后,为设备设置属性DO_DIRECT_IO

和缓冲读写设备不同,直接方式读写设备,操作系统会将用户模式下的缓冲区锁住。然后操作系统将这段缓冲区在内核模式地址空间中再映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存。无论操作系统如何切换进程,内核模式地址保持不变。

操作系统(IO管理器)先将用户模式的地址锁定后,操作系统(IO管理器)用内存描述符表(MDL 数据结构)记录这段内存。如下图。用户模式的这段缓冲区在虚拟内存上是连续的,但是在物理内存上可能是离散的。

MDL记录这段虚拟内存,这段虚拟内存的大小存储在Mdl->ByteCount里,这段虚拟内存所在第一个页首地址是Mdl->StartVa,这段虚拟内存的首地址对于第一个页首地址的偏移量是Mdl->ByteOffset。因此,这段虚拟内存的首地址应该是:

Mdl->StartVa + Mdl->ByteOffset

下面是DDK定义的几个宏,方便我们得到这几个数值。

#define MmGetMdlByteCount(Mdl) ((Mdl)->ByteCount)

#define MmGetMdlByteOffset(Mdl) ((Mdl)->ByteOffset)

#define MmGetMdlVirtualAddress(Mdl) \

     ((PVOID)((PCHAR)((Mdl)->StartVa) + ((Mdl)->ByteOffset))

例子:

NTSTATUS Read(IN PDEVICE_OBJECT   DeviceObject,
			    IN PIRP             Irp)

{

	PIO_STATCK_LOCATION IrpSp;
	ULONG ReadLength;
	ULONG MdlLength;
	ULONG MdlOffset;
	PVOID MdlAddress;
	ULONG KernelAddress;
	// 获得当前堆栈单元
	IrpSp = IoGetCurrentIrpStackLocation(Irp);
	// 获取应用层要读的字节数
	ReadLength = IrpSp->Parameters.Read.Length;
	// 得到应用层被锁定的虚拟缓冲区的长度
	MdlLength = MmGetMdlByteCount(Irp->MdlAddress);
	// 得到锁定缓冲区的偏移量
	MdlOffset = MmGetMdlByteOffset(Irp->MdlAddress);
	// 得到锁定缓冲区的虚拟地址
	MdlAddress = MmGetMdlByteOffset(Irp->MdlAddress);
	// 获得虚拟缓冲区在内核模式下的映射地址
	KernelAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress);
	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest(Irp,IO_NO_INCREMENT);
	retrun STATUS_SUCCESS;
}






 

你可能感兴趣的:(zz: 设备读写 之 直接方式(Direct I/O)MmGetMdlByteCount/MmGetMdlByteOffset/MmGetMdlVirtualAddress)