- 注册时间
- 2011-3-6
- 最后登录
- 1970-1-1
该用户从未签到
|
很多时候我们需要在应用层和驱动层之间进行数据传输,当通过事件或者内核对象来进行同步的话,满足不了速率要求,特别是对于通讯类软件更无法忍受。总结了一下,驱动层与用户层之间共享内存的最简单容易实现的方法有两种:
用户通过DeviceIoControl发送一个IOCTL给驱动层,同时传入一个指向内存的指针,驱动层就能通过这个指针来实现内存共享了。
驱动层分配内存页面,并将内存页面映射到指定用户模式进程的地址空间,通过IOCTL将地址传递到应用层,即可实现内存共享。
下面来详细说说这两种方法的实现
通过DeviceIoControl传递控制码的时候,可以指定传输方式。具体包括:METHOD_IN_DIRECT/METHOD_OUT_DIRECT/METHOD_NEITHER。
在前两种传输模式下,IO管理器会为我们创建一个MDL锁住该应用层的缓冲区内存,然后,我们可以在内核层中使用MmGetSystemAddressForMdlSafe获得该缓冲区所对应的内存的地址。相当于此时对于同一份数据,有两份映像文件同时指引着。一份在ring3,另一份在ring0.
PUCHAR pBuffer = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress);
使用METHOD_NEITHER则有很多限制。其中最主要的规则是驱动发起请求进程的上下文中存取buffer,这是因为我们必须通过buffer的用户虚拟地址空间存取buffer.我个人的理解是IO管理器此时没有为我们的应用层缓冲区加一个MDL锁,以至于应用层有可能切换到另外的进程。而此时若再通过该地址存取的话,有可能造成崩溃。因而,驱动必须在设备栈的最顶端,由应用层序经IO管理器直接调用。
注意:对于前两种情况,有可能出现ring3应用程序突然退出的情况、或者irp被完成而此时驱动层再通过这个地址来访问共享内存的时候,就会造成崩溃了。一般的解决方案都是设置IRP cancle例程。挂起irp,返回STATUS_PENDING,将irp插入等待队列即可。
总之一句话,尽量避免使用METHOD_NEITHER.其他的通过完成例程、同步事件来处理。
2. ring0分配页面。没有想象中那么复杂,通过几个内核API就能简单实现,而且可以保证驱动层对共享区拥有最有效的控制权。
PVOID CreateAndMapMemory(OUT PMDL* PMemMdl,
OUT PVOID* UserVa)
{
PMDL Mdl;
PVOID UserVAToReturn;
PHYSICAL_ADDRESS LowAddress;
PHYSICAL_ADDRESS HighAddress;
SIZE_T TotalBytes;
// 初始化MmAllocatePagesForMdl需要的Physical Address
LowAddress.QuadPart = 0;
MAX_MEM(HighAddress.QuardPart);
TotalBytes.QuadPart = PAGE_SIZE;
// 分配4K的共享缓冲区
Mdl = MmAllocatePagesForMdl(LowAddress,
HighAddress,
LowAddress,
TotalBytes);
if(!Mdl)
{
Return STATUS_INSUFFICIENT_RESOURCES;
}
// 映射共享缓冲区到用户地址空间
UserVAToReturn = MmMapLockedPagesSpecifyCache(Mdl,
UserMode,
MmCached,
NULL,
FALSE,
NormalPagePriority);
if(!UserVAToReturn)
{
MmFreePagesFromMdl(Mdl);
IoFreeMdl(Mdl);
Return STATUS_INSUFFICIENT_RESOURCE;
}
// 返回,得到MDL和用户层的虚拟地址
*UserVa = UserVAToReturn;
*PMemMdl = Mdl;
return STATUS_SUCCESS;
}
VOID UnMapAndFreeMemory(PMDL PMdl,PVOID UserVa)
{
if(!PMdl)
{ return ;}
// 解除映射
MmUnMapLockerPages(UserVa,PMdl);
// 释放MDL锁定的物理页
MmFreePagesFromMdl(PMdl);
// 释放MDL
IoFreeMdl(PMdl);
}
几个比较有用的API需要记录一下,具体内容请参考WDK帮助文档:
MmAllocatePagesForMdl:The MmAllocatePagesForMdl routine allocates zero-filled, nonpaged, physical memory pages to an MDL.
MmMapLockedPagesSpecifyCache:The MmMapLockedPagesSpecifyCache routine maps the physical pages that are described by an MDL to a virtual address, and enables the caller to specify the cache attribute that is used to create the mapping.
MmUnmapLockedPages:The MmUnmapLockedPages routine releases a mapping that was set up by a preceding call to the MmMapLockedPages or MmMapLockedPagesSpecifyCache routine.
MmFreePagesFromMdl:The MmFreePagesFromMdl routine frees all the physical pages that are described by an MDL that was created by the MmAllocatePagesForMdl routine.
IoFreeMdl:The IoFreeMdl routine releases a caller-allocated memory descriptor list (MDL).
好吧,先到这里。至于底层实现方面,等小弟看完WRK的相关部分在写成文档。 |
|