win32下进程间通信——共享内存
一、引言
在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换.
进程间通讯(即:同机通讯)和数据交换有多种方式:消息、共享内存、匿名(命名)管道、邮槽、Windows套接字等多种技术。“共享内存”(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间。例如:如果两个进程使用相同的DLL,只把DLL的代码页装入内存一次,其他所有映射这个DLL的进程只要共享这些代码页就可以了;利用消息机制实现IPC虽然有交换的数据量小、携带的信息少等缺点,但由于其实现方便、应用灵活而广泛应用于无须大量、频繁数据交换的内部进程通讯系统之中。
二、同机进程间共享内存的实现
采用内存映射文件实现WIN32进程间的通讯:Windows中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保留一段内存区域,把硬盘或页文件上的目标文件映射到这段虚拟内存中。注意:在程序实现中必须考虑各进程之间的同步问题。
具体实现步骤如下:
1、在服务器端进程中调用内存映射API函数CreateFileMapping创建一个有名字标识的共享内存;
函数CreateFileMapping原型
HANDLE CreateFileMapping (
HANDLE hFile, // 映射文件的句柄,若设为0xFFFFFFFF(即:INVALID_HANDLE_VALUE)则创建一个进程间共享的对象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, //安全属性
DWORD flProtect, //保护方式
DWORD dwMaximumSizeHigh, //对象的大小
DWORD dwMaximumSizeLow,
LPCTSTR lpName // 映射文件名,即共享内存的名称 );
与虚拟内存类似,保护方式参数可以是PAGE_READONLY或是PAGE_READWRITE。如果多进程都对同一共享内存进行写访问,则必须保持相互间同步。映射文件还可以指定PAGE_WRITECOPY标志,可以保证其原始数据不会遭到破坏,同时允许其他进程在必要时自由的操作数据的拷贝。
例如:创建一个名为“zzj”的长度为4096字节的有名映射文件:
HANDLE m_hMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000," zzj");
2、在创建文件映射对象后,服务器端进程调用MapViewOfFile函数映射到本进程的地址空间内;
例:映射缓存区视图
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
3、客户端进程访问共享内存对象,需要通过内存对象名调用OpenFileMapping函数,以获得共享内存对象的句柄
HANDLE m_hMapFile =OpenFileMapping(FILE_MAP_WRITE,FALSE," zzj");
4、如果客户端进程获得共享内存对象的句柄成功,则调用MapViewOfFile函数来映射对象视图。用户可以使用该对象视图来进行数据读写操作,以达到数据通讯的目的。
例:映射缓存区视图
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
5、当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图:
if (m_pBaseMapFile)
{
UnmapViewOfFile(m_pBaseMapFile);
SharedMapView=NULL;
}
三、使用文件映射实现共享内存。
FileMapping用于将存在于磁盘的文件放进一个进程的虚拟地址空间,并在该进程的虚拟地址空间中产生一个区域用于“存放”该文件,这个空间就叫做File View(存放在进程的虚拟内存中),系统并同时产生一个File Mapping Object(存放于物理内存中)用于维持这种映射关系,这样当多个进程需要读写那个文件的数据时,它们的File View其实对应的都是同一个File Mapping Object,这样做可节省内存和保持数据的同步性,并达到数据共享的目的。
当然在一个应用向文件中写入数据时,其它进程不应该去读取这个正在写入的数据。这就需要进行一些同步的操作。下边来看一下具体的API。
CreateFileMaping的用法:
HANDLE CreateFileMapping( //返回FileMapping Object的句柄
HANDLE hFile, //想要产生映射的文件的句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全属性(只对NT和2000生效)
DWORD flProtect, //保护标致
DWORD dwMaximumSizeHigh, //在DWORD的高位中存放
File Mapping Object //的大小
DWORD dwMaximumSizeLow, //在DWORD的低位中存放
File Mapping Object //的大小(通常这两个参数有一个为0)
LPCTSTR lpName //File Mapping Object的名称。
);
1)物理文件句柄
任何可以获得的物理文件句柄,如果你需要创建一个物理文件无关的内存映射也无妨,将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了.
如果需要和物理文件关联,要确保你的物理文件创建的时候的访问模式和"保护设置"匹配,比如:物理文件只读,内存映射需要读写就会发生错误。推荐你的物理文件使用独占方式创建。
如果使用 INVALID_HANDLE_VALUE,也需要设置需要申请的内存空间的大小,无论物理文件句柄参数是否有效,这样 CreateFileMapping就可以创建一个和物理文件大小无关的内存空间给你,甚至超过实际文件大小,如果你的物理文件有效,而大小参数为0,则返回给你的是一个和物理文件大小一样的内存空间地址范围。返回给你的文件映射地址空间是可以通过复制,集成或者命名得到,初始内容为0。
2)保护设置
就是安全设置,不过一般设置NULL就可以了,使用默认的安全配置. 在win2k下如果需要进行限制,这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是,可以考虑进行限制.
3)高位文件大小
32位地址空间,设置为0。
4) 共享内存名称
命名可以包含 "Global"或者 "Local" 前缀在全局或者会话名空间初级文件映射.其他部分可以包含任何除了()以外的字符,可以参考 Kernel Object Name Spaces.
5)调用CreateFileMapping的时候GetLastError的对应错误
ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射,应有此报
ERROR_INVALID_HANDLE 如果发现你的命名内存空间和现有的内存映射,互斥量,信号量,临界区同名就麻烦了
ERROR_ALREADY_EXISTS 表示内存空间命名已经存在
使用函数CreateFileMapping创建一个想共享的文件数据句柄,然后使用MapViewOfFile来获取共享的内存地址,然后使用OpenFileMapping函数在另一个进程里打开共享文件的名称,这样就可以实现不同的进程共享数据。
代码示例:这个程序包括一个客户端和一个服务端,服务端创建共享内存,客户端打开共享内存,两者通过两个事件互斥访问共享内存,实现一个小功能,就是服务端进程从控制台读入数据发送给客户端进程。
服务端:
- #include "stdafx.h"
- #include <Windows.h>
- #include <iostream>
- using namespace std;
- int main()
- {
- HANDLE hMutex = NULL;
- HANDLE hFileMapping = NULL;
- LPVOID lpShareMemory = NULL;
- HANDLE hServerWriteOver = NULL;
- HANDLE hClientReadOver = NULL;
- //create share memory
- hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
- NULL,
- PAGE_READWRITE,
- 0,
- 1024*1024,
- L"ShareMemoryTest");
- if (NULL == hFileMapping)
- {
- cout << "CreateFileMapping fail:" << GetLastError() << endl;
- goto SERVER_SHARE_MEMORY_END;
- }
- lpShareMemory = MapViewOfFile(hFileMapping,
- FILE_MAP_ALL_ACCESS,
- 0,
- 0, //memory start address
- 0); //all memory space
- if (NULL == lpShareMemory)
- {
- cout << "MapViewOfFile" << GetLastError() << endl;
- goto SERVER_SHARE_MEMORY_END;
- }
- //373
- hMutex = CreateMutex(NULL, FALSE, L"SM_Mutex");
- if (NULL == hMutex || ERROR_ALREADY_EXISTS == GetLastError())
- {
- cout << "CreateMutex" << GetLastError() << endl;
- goto SERVER_SHARE_MEMORY_END;
- }//多个线程互斥访问
- //send data
- hServerWriteOver = CreateEvent(NULL,
- TRUE,
- FALSE,
- L"ServerWriteOver");
- hClientReadOver = CreateEvent(NULL,
- TRUE,
- FALSE,
- L"ClientReadOver");
- if (NULL == hServerWriteOver ||
- NULL == hClientReadOver)
- {
- cout << "CreateEvent" << GetLastError() << endl;
- goto SERVER_SHARE_MEMORY_END;
- }
- char p = 0;
- char* q = (char*)lpShareMemory;
- do
- {
- p = getchar();
- if (WaitForSingleObject(hClientReadOver, 5*1000) != WAIT_OBJECT_0)
- goto SERVER_SHARE_MEMORY_END;
- q[0] = p;
- if (!ResetEvent(hClientReadOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件对象设置为无信号状态
- if (!SetEvent(hServerWriteOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件对象设置为有信号状态
- } while (p != '\n');
- SERVER_SHARE_MEMORY_END:
- //release share memory
- if (NULL != hServerWriteOver) CloseHandle(hServerWriteOver);
- if (NULL != hClientReadOver) CloseHandle(hClientReadOver);
- if (NULL != lpShareMemory) UnmapViewOfFile(lpShareMemory);
- if (NULL != hFileMapping) CloseHandle(hFileMapping);
- if (NULL != hMutex) ReleaseMutex(hMutex);
- return 0;
- }
客户端:
- #include "stdafx.h"
- #include <Windows.h>
- #include <iostream>
- using namespace std;
- int main()
- {
- HANDLE hMutex = NULL;
- HANDLE hFileMapping = NULL;
- LPVOID lpShareMemory = NULL;
- HANDLE hServerWriteOver = NULL;
- HANDLE hClientReadOver = NULL;
- hMutex = OpenMutex(MUTEX_ALL_ACCESS,
- FALSE,
- L"SM_Mutex");
- if (NULL == hMutex)
- {
- if (ERROR_FILE_NOT_FOUND == GetLastError())
- {
- cout << "OpenMutex fail: file not found!" << endl;
- goto CLIENT_SHARE_MEMORY_END;
- }
- else
- {
- cout << "OpenMutex fail:" << GetLastError() << endl;
- goto CLIENT_SHARE_MEMORY_END;
- }
- }
- if (WaitForSingleObject(hMutex, 5000) != WAIT_OBJECT_0)//hMutex 一旦互斥对象处于有信号状态,则该函数返回
- {
- DWORD dwErr = GetLastError();
- goto CLIENT_SHARE_MEMORY_END;
- }
- //open share memory
- hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,
- FALSE,
- L"ShareMemoryTest");
- if (NULL == hFileMapping)
- {
- cout << "OpenFileMapping" << GetLastError() << endl;
- goto CLIENT_SHARE_MEMORY_END;
- }
- lpShareMemory = MapViewOfFile(hFileMapping,
- FILE_MAP_ALL_ACCESS,
- 0,
- 0,
- 0);
- if (NULL == lpShareMemory)
- {
- cout << "MapViewOfFile" << GetLastError() << endl;
- goto CLIENT_SHARE_MEMORY_END;
- }
- //read and write data
- hServerWriteOver = CreateEvent(NULL,
- TRUE,
- FALSE,
- L"ServerWriteOver");
- hClientReadOver = CreateEvent(NULL,
- TRUE,
- FALSE,
- L"ClientReadOver");
- if (NULL == hServerWriteOver ||
- NULL == hClientReadOver)
- {
- cout << "CreateEvent" << GetLastError() << endl;
- goto CLIENT_SHARE_MEMORY_END;
- }
- char p = 0;
- char* q = (char*)lpShareMemory;
- do
- {
- if (!SetEvent(hClientReadOver))
- goto CLIENT_SHARE_MEMORY_END;
- if (WaitForSingleObject(hServerWriteOver, INFINITE) != WAIT_OBJECT_0)
- goto CLIENT_SHARE_MEMORY_END;
- p = q[0];
- putchar(p);
- if (!ResetEvent(hServerWriteOver))
- goto CLIENT_SHARE_MEMORY_END;
- } while (p != '\n');
- CLIENT_SHARE_MEMORY_END:
- //release share memory
- if (NULL != hServerWriteOver) CloseHandle(hServerWriteOver);
- if (NULL != hClientReadOver) CloseHandle(hClientReadOver);
- if (NULL != lpShareMemory) UnmapViewOfFile(lpShareMemory);
- if (NULL != hFileMapping) CloseHandle(hFileMapping);
- if (NULL != hMutex) ReleaseMutex(hMutex);
- return 0;
- }
win32下进程间通信——共享内存的更多相关文章
- Linux下进程间通信--共享内存:最快的进程间通信方式
共享内存: 一.概念: 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间. 进程A可以即时看到进程B ...
- Linux环境进程间通信: 共享内存
Linux环境进程间通信: 共享内存 第一部分 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进 ...
- C# 进程间通信(共享内存)
原文:C# 进程间通信(共享内存) 进程间通信的方式有很多,常用的方式有: 1.共享内存(内存映射文件,共享内存DLL). 2.命名管道和匿名管道. 3.发送消息 本文是记录共享内存的方式进行进程间通 ...
- Windows进程间通信--共享内存映射文件(FileMapping)--VS2012下发送和接收
之前以为两个互不相关的程序a.exe b.exe通信就只能通过网络,人家说可以通过发消息,我还深以为不然,对此,我表示万分惭愧. 之前课本上说的进程间通信,有共享内存.管道等之类的,但没有自己操刀写过 ...
- [转]Windows环境下利用“共享内存”实现进程间通信的C/C++代码---利用CreateFileMapping和MapViewOfFile
http://blog.csdn.net/stpeace/article/details/39534361 进程间的通信方式有很多种, 上次我们说了最傻瓜的“共享外存/文件”的方法. 那么, 在本文中 ...
- Linux进程间通信—共享内存
五.共享内存(shared memory) 共享内存映射为一段可以被其他进程访问的内存.该共享内存由一个进程所创建,然后其他进程可以挂载到该共享内存中.共享内存是最快的IPC机制,但由于linux本身 ...
- linux 进程间通信 共享内存 shmat
系统调用mmap()通过映射一个普通文件实现共享内存.系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信.也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shm ...
- linux 进程间通信 共享内存 mmap
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存中数据的更新,反 ...
- linux进程间通信-共享内存
转载:http://www.cnblogs.com/fangshenghui/p/4039720.html 一 共享内存介绍 共享内存可以从字面上去理解,就把一片逻辑内存共享出来,让不同的进程去访问它 ...
随机推荐
- 给id赋值
var div = document.getElementByTagName('div') div.id="mydiv";div.setAttribute("id&quo ...
- C# Windows服务安装出现System.Security.SecurityException异常解决办法
我把注册windows服务所用的安装及启用服务命令写到了bat可执行文件(名称为install.bat)中,如下所示: %SystemRoot%\Microsoft.NET\Framework\v4. ...
- Linux之Vim编辑器使用
vim文本编辑器用于建立 编辑 显示文本文件,vim没有菜单,只有命令 在windows 平台下可使用gvim进行编写 Vim三种工作模式: 常有命令: 1.INSERT插入命令 i 在光标前插入 I ...
- JQuery jsonp使用小记
在一个不支持PHP的主机上,需要对某些页面做访问统计.我的方案是在静态的HTML页面上,用JSONP向能够执行PHP的主机进行跨域请求,从而使用PHP解决这个访问量统计问题. 在服务器端,PHP页面返 ...
- openerp binary filed import export
1: user xmlrpc 2: use csv file to export import but want to change the csv model field_size_limit
- 【转】app后端如何选择合适的数据库产品
转自:http://blog.csdn.net/newjueqi/article/details/44003503 app后端的开发中,经常要面临的一个问题是:数据放在哪里? mysql ?redis ...
- Redmine配置
官网步骤说明 http://www.redmine.org/projects/redmine/wiki/RedmineInstall 搭环境 1.MySql 2.RailsInstaller:Redm ...
- Application路径
根目录:StreamingAssets文件夹 #if UNITY_EDITOR string filepath = Application.dataPath +"/StreamingAsse ...
- DaoImpl中实现查询分页-使用HibernateCallback来做更加方便
/** * */ package com.wolfgang.dao; import java.sql.SQLException; import java.util.List; import org.h ...
- activiti入门3排他网关,并行网管,包含网关,事件网关(转)
网关用来控制流程的流向 网关可以消费也可以生成token. 网关显示成菱形图形,内部有有一个小图标. 图标表示网关的类型. 基本分支 首先 利用 流程变量 写个带有分支的一个基本流程 流程图: 部署 ...