看流星社区

 找回密码
 注册账号
查看: 1830|回复: 0

C++共享内存(实现篇)

[复制链接]

该用户从未签到

发表于 2016-9-21 11:09:41 | 显示全部楼层 |阅读模式
很多时候我们需要在应用层和驱动层之间进行数据传输,当通过事件或者内核对象来进行同步的话,满足不了速率要求,特别是对于通讯类软件更无法忍受。总结了一下,驱动层与用户层之间共享内存的最简单容易实现的方法有两种:

用户通过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的相关部分在写成文档。
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 11:20

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表