需求:需要分配约1GB的空间,设备通过系统DMA(设备没有DMA控制器,故采用Slave DMA)写入此空间中。运行环境:Win7 x64
方案一: 通过DirectIO方式,使用Scatter/Gatter方式、调用 WdfDmaTransactionInitializeUsingRequest进行分配。
问题:分配的多个分页中,有部分指定的地址不再内存范围内,在硬盘上。 偶发部分地址不能写入。 通过屏蔽这样的地址可以解决此问题,但会浪费一些地址段,且大小不可控。
在32位系统上,分配的是0-4G范围内的值,但在x64不能应用此设置,会导致混乱。
由于考虑到避免应用程序对驱动的影响,考虑在驱动加载时,分配指定的空间,这样即使应用程序异常关闭,也不影响设备的读写。
测试一: 在EvtDevicePrepareHardware函数中,使用WdfCommonBufferCreate进行分配。 测试结果:分配不能超出30M,测试2M可以。
测试二: 在EvtDevicePrepareHardware函数中,使用WdfDmaEnablerWdmGetDmaAdapter进行分配。 测试结果:分配1M,蓝屏;1GB,提示分配失败。
测试三: 在EvtDevicePrepareHardware函数中,使用WdfMemoryCreate或ExAllocatePoolWithTag进行分配。 测试结果:可以分配1GB数据,但部分分页在硬盘上。 另外使用ExAllocatePoolWithTag,在台式机上,分配在硬盘上的概率很小,但在另外一台工控机上,竟然大部分都在硬盘上。
测试四: 在EvtDevicePrepareHardware函数中,使用MmAllocateContiguousMemory进行分配。 测试结果:分配约400MB,蓝屏。有朋友测试过30MB可以。
测试五: 在EvtDevicePrepareHardware函数中,使用MmAllocatePagesForMdl进行分配,并指定其分配范围。
PHYSICAL_ADDRESS LowAddress;
PHYSICAL_ADDRESS HighAddress;
PHYSICAL_ADDRESS SkipAddress;
LowAddress.QuadPart = 0;
HighAddress.QuadPart = 0x00000001ffffffffI64; //8G以内 SkipAddress.QuadPart = 0;
pDeviceContext->Mdl = MmAllocatePagesForMdl(LowAddress, HighAddress, SkipAddress, MAXNLEN );
测试结果:均分配在指定范围内,默认从最大内存值向前依次分配。很方便的满足了DMA传输要求。
另外,如果需要连续的地址段,可以使用MmAllocatePagesForMdlEx方法。