共享内存的使用

来源:互联网 发布:matlab矩阵绘制散点图 编辑:程序博客网 时间:2024/04/29 22:54

在Windows操作系统下,任何一个进程不允许读取、写入或是修改另一个进程的数据(包括变量、对象和内存分配等),但是在某个进程内创建的文件映射对象的视图却能够为多个其他进程所映射,这些进程共享的是物理存储器的同一个页面。因此,当一个进程将数据写入此共享文件映射对象的视图时,其他进程可以立即获取数据变更情况。为了进一步提高数据交换的速度,还可以采用由系统页文件支持的内存映射文件而直接在内存区域使用,显然这种共享内存的方式是完全可以满足在进程间进行大数据量数据快速传输任务要求的。下面给出在两个相互独立的进程间通过文件映射对象来分配和访问同一个共享内存块的应用实例。在本例中,由发送方程序负责向接收方程序发送数据,文件映射对象由发送方创建和关闭,并且指定一个唯一的名字供接收程序使用。接收方程序直接通过这个唯一指定的名字打开此文件映射对象,并完成对数据的接收。

  在发送方程序中,首先通过CreateFileMapping()函数创建一个内存映射文件对象,如果创建成功则通过MapViewOfFile()函数将此文件映射对象的视图映射进地址空间,同时得到此映射视图的首址。可见,共享内存的创建主要是通过这两个函数完成的。这两个函数原形声明如下:

HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName);
LPVOID MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);

  CreateFileMapping()函数参数hFile指定了待映射到进程地址空间的文件句柄,如果为无效句柄则系统会创建一个使用来自页文件而非指定磁盘文件存储器的文件映射对象。很显然,在本例中为了数据能快速交换,需要人为将此参数设定为INVALID_HANDLE_VALUE;参数flProtect设定了系统对页面采取的保护属性,由于需要进行读写操作,因此可以设置保护属性PAGE_READWRITE;双字型参数dwMaximumSizeHigh和dwMaximumSizeLow指定了所开辟共享内存区的最大字节数;最后的参数lpName用来给此共享内存设定一个名字,接收程序可以通过这个名字将其打开。MapViewOfFile()函数的参数hFileMappingObject为CreateFileMapping()返回的内存文件映像对象句柄;参数dwDesiredAccess再次指定对其数据的访问方式,而且需要同CreateFileMapping()函数所设置的保护属性相匹配。这里对保护属性的重复设置可以确保应用程序能更多的对数据的保护属性进行有效控制。下面给出创建共享内存的部分关键代

hRecvMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1000000, "DataMap");
if (hRecvMap != NULL)
{
lpData = (LPBYTE)MapViewOfFile(hRecvMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpData == NULL)
{
CloseHandle(hRecvMap);
hRecvMap = NULL;
}
}
// 通知接收程序内存文件映射对象的视图已经打开
HWND hRecv = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hRecv != NULL)
::PostMessage(hRecv, WM_MAP_OPEN, 0, 0);

  数据的传送实际是将数据从发送方写到共享内存中,然后由接收程序及时从中取走即可。数据从发送方程序写到共享内存比较简单,只需用memcpy()函数将数据拷贝过去,关键在于能及时通知接收程序数据已写入到共享内存,并让其即使取走。在这里仍采取消息通知的方式,当数据写入共享内存后通过PostMessage()函数向接收方程序发送消息,接收方在消息响应函数中完成对数据的读取:

// 数据复制到共享内存
memcpy(lpData, RecvBuf, sizeof(RecvBuf));
// 通知接收方接收数据
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_DATA_READY, (WPARAM)0, (LPARAM)sizeof(RecvBuf));

  当数据传输结束,即将退出程序时,需要将映射进来的内存文件映射对象视图卸载和资源的释放等处理。这部分工作主要由UnmapViewOfFile()和CloseHandle()等函数完成:

HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM);
if (hDeCode != NULL)
::PostMessage(hDeCode, WM_MAP_CLOSE, 0, 0);
if (lpData != NULL)
{
UnmapViewOfFile(lpData);
lpData = NULL;
}
if (hRecvMap != NULL)
{
CloseHandle(hRecvMap);
hRecvMap = NULL;
}

  在接收程序中,在收到由发送放发出的WM_MAP_OPEN消息后,由OpenFileMapping()函数打开由名字"DataMap"指定的文件映射对象,如果执行成功,继续用MapViewOfFile()函数将此文件映射对象的视图映射到接收应用程序的地址空间并得到其首址:

m_hReceiveMap = OpenFileMapping(FILE_MAP_READ, FALSE, "DataMap");
if (m_hReceiveMap == NULL)
return;
m_lpbReceiveBuf = (LPBYTE)MapViewOfFile(m_hReceiveMap,FILE_MAP_READ,0,0,0);
if (m_lpbReceiveBuf == NULL)
{
CloseHandle(m_hReceiveMap);
m_hReceiveMap=NULL;
}

  当发送方程序将数据写入到共享内存后,接收方将收到消息WM_DATA_READY,在响应函数中将数据从共享内存复制到本地缓存中,再进行后续的处理。同发送程序类似,在接收程序数据接收完毕后,也需要用UnmapViewOfFile()、CloseHandle()等函数完成对文件视图等打开过资源的释放:

// 从共享内存接收数据
memcpy(RecvBuf, (char*)(m_lpbReceiveBuf), (int)lParam);
……
// 程序退出前资源的释放
UnmapViewOfFile(m_lpbReceiveBuf);
m_lpbReceiveBuf = NULL;
CloseHandle(m_hReceiveMap);
m_hReceiveMap = NULL;

  小结

  经实际测试,使用共享内存在处理大数据量数据的快速交换时表现出了良好的性能,在数据可靠性等方面要远远高于发送WM_COPYDATA消息的方式。这种大容量、高速的数据共享处理方式在设计高速数传通讯类软件中有着很好的使用效果。本文所述代码在Windows 2000下由Microsoft Visual C++ 6.0编译通过。

//附:在dll中共享数据,在宿主进程中读取共享的数据;以下程序在WinXP Pro + VC6 通过测试

//dll创建共享数据;

// create mapping file

HANDLE hSockSrvRecMap;
LPBYTE lpData;
char DataBuf[128];


hSockSrvRecMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1024, "SockSrvDataMap");
if (hSockSrvRecMap != NULL)
{
   lpData = (LPBYTE)MapViewOfFile(hSockSrvRecMap, FILE_MAP_WRITE, 0, 0, 0);
   if (lpData == NULL)
   {
    CloseHandle(hSockSrvRecMap);
    hSockSrvRecMap = NULL;
   }
}
for (int i=0; i<128; i++) {
   DataBuf[i] = i;
}
memcpy(lpData,DataBuf,sizeof(DataBuf));

//宿主进程读取共享数据;

m_hReceiveMap = OpenFileMapping(FILE_MAP_READ, FALSE, "SockSrvDataMap");
if (m_hReceiveMap == NULL)
   return;
m_lpbReceiveBuf = (LPBYTE)MapViewOfFile(m_hReceiveMap,FILE_MAP_READ,0,0,0);
if (m_lpbReceiveBuf == NULL)
{
   CloseHandle(m_hReceiveMap);
   m_hReceiveMap=NULL;
}
for (int i=0; i<18; i++) {
   TRACE("%d ",*(m_lpbReceiveBuf++));
}
TRACE("/n mapping data dump complete./n");

// 不需要内存映射的时候要关闭,两边都需要关闭内存映射文件

if (m_lpbReceiveBuf != NULL)
{
   UnmapViewOfFile(m_lpbReceiveBuf);
   m_lpbReceiveBuf = NULL;
}
if (m_hReceiveMap != NULL)
{
   CloseHandle(m_hReceiveMap);
   m_hReceiveMap = NULL;
}

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 斗鱼直播没人看怎么办 淘宝直播间没人气怎么办 挂水了还是有热度怎么办 陌陌工会不结算工资怎么办 滴滴给了差评怎么办 饿了么星级低怎么办 滴滴乘客给低星怎么办 蘑菇街自动收货前还没到怎么办 小主播人气太少别人看不到怎么办 税收分类编码不可用怎么办 斗鱼鱼翅充错了怎么办 苹果指纹摔坏了怎么办 小米5指纹坏了怎么办 苹果5s指纹失灵怎么办 学生赌博输了3万怎么办 电脑录屏没有声音怎么办 别人说你没有他美怎么办 没有你我怎么办歌词是什么意思 要是没有他我怎么办啊歌词 用喀秋莎保存的视频黑屏怎么办 电脑杀毒之后开不了机怎么办 夫妻离婚分房分车怎么办 请的护身符丢了怎么办 老车轻微烧机油怎么办 电脑下软件变卡怎么办 机械表日历偏了怎么办 子宫内膜异位痛经怎么办 凉着了坏肚子怎么办 昆虫叮咬后疼痛起水泡怎么办 每次来月经都痛经怎么办 人左肩的灯灭了怎么办 香港超过7天了怎么办? 手机拍完照图像是倒的怎么办 地下城金币邮寄卡了怎么办 wow7.3打团本卡怎么办 上古卷轴5出bug怎么办 苹果手机打游戏太卡怎么办 梦幻将军令换了怎么办 手机丢了将军令怎么办 大军之印放弃了怎么办 联想一体机进入界面就死机怎么办