【c++windows共享内存(内存映射)】
共享内存是最快的IPC方式读写一轮需要两次拷贝:用户空间->共享内存,共享内存->用户空间共享内存一般通过memcpy()进行拷贝,因此共享内存数据并不会自动清空CreateFileMapping()第一个参数INVALID_HANDLE_VALUE时,实际是在内核内存创建一个磁盘无关的内存,不属于任何进程空间里的,不使用时需要释放(任何进程都可拿着句柄去释放?有待验证);
·
- 共享内存是最快的IPC方式
- 读写一轮需要两次拷贝:用户空间->共享内存,共享内存->用户空间
- 共享内存一般通过memcpy()进行拷贝,因此共享内存数据并不会自动清空
- CreateFileMapping()第一个参数INVALID_HANDLE_VALUE时,实际是在内核内存创建一个磁盘无关的内存,不属于任何进程空间里的,不使用时需要释放(任何进程都可拿着句柄去释放?有待验证);当第一个参数不为INVALID_HANDLE_VALUE时,而是磁盘实际存在的文件时,系统同样会在内核申请一块内存,然后返回该文件的内存映射文件对象实例(大小,安全,访问权限,名字等),MapViewOfFile()也是将实际内存地址和进程空间地址作映射
- MapViewOfFile()是将共享内存的地址映射到进程的地址空间里,进程里操作返回的地址/指针/句柄(通过页表寻址到共享内存)实际就等于直接操作了内存
缺点:
同步/异步问题需要用户自己控制,复杂度随操作对象增加而增加
进程间通信
**说明:**服务端写,客户端读
一共创建了两个event,一个服务写就绪hEventSrv,一个客户端读就绪hEventClient
服务端代码
// ConsoleAppShareMemSrv.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ConsoleAppShareMemSrv.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 唯一的应用程序对象
CWinApp theApp;
using namespace std;
DWORD dwSize = 1024 * 10;
HANDLE hEventSrv = NULL;
HANDLE hEventClient = NULL;
int main()
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(nullptr);
if (hModule != nullptr)
{
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
wprintf(L"错误: MFC 初始化失败\n");
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
HANDLE hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, NULL, dwSize, TEXT("shareMemory"));
if (!hFileMapping)
{
int err = GetLastError();
printf("CreateFileMapping failed,error code: %d", err);
return -1;
}
LPVOID lpMapView = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, NULL, NULL, NULL);
if (!lpMapView)
{
int err = GetLastError();
printf("MapViewOfFile failed,error code: %d", err);
return -1;
}
hEventSrv = CreateEvent(NULL, TRUE, TRUE, TEXT("shareEventSrv"));
if (!hEventSrv)
{
int err = GetLastError();
printf("CreateEvent failed,error code: %d", err);
return -1;
}
hEventClient = CreateEvent(NULL, TRUE, FALSE, TEXT("shareEventClient"));
if (!hEventClient)
{
int err = GetLastError();
printf("CreateEvent failed,error code: %d", err);
return -1;
}
while (WAIT_OBJECT_0 == WaitForSingleObject(hEventSrv, INFINITE)) //等待服务端可写,阻塞
{
char p[1024] = {0};
cin.getline(p, sizeof(p));
int len = strlen(p);
int len2 = sizeof(p);
memcpy(lpMapView, p, len2); //选用len只拷贝实际字节数,即没有被重新覆盖的内容,仍存在内存里;len2是将1024都cp到内存,全部覆盖
ResetEvent(hEventSrv); //将服务写重置为false
SetEvent(hEventClient); //将客户端读置为true
Sleep(1500);
}
}
}
else
{
// TODO: 更改错误代码以符合您的需要
wprintf(L"错误: GetModuleHandle 失败\n");
nRetCode = 1;
}
system("pause");
return nRetCode;
}
客户端代码
// ConsoleAppShareMemClient.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ConsoleAppShareMemClient.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 唯一的应用程序对象
CWinApp theApp;
using namespace std;
DWORD dwSize = 1024 * 10;
HANDLE hEventSrv = NULL;
HANDLE hEventClient = NULL;
int main()
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(nullptr);
if (hModule != nullptr)
{
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
wprintf(L"错误: MFC 初始化失败\n");
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE , TEXT("shareMemory"));
if (!hFileMapping)
{
int err = GetLastError();
printf("OpenFileMapping failed,error code: %d", err);
return -1;
}
LPVOID lpMapView = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, NULL, NULL, NULL);
if (!lpMapView)
{
int err = GetLastError();
printf("MapViewOfFile failed,error code: %d", err);
return -1;
}
hEventSrv = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("shareEventSrv"));
if (!hEventSrv)
{
int err = GetLastError();
printf("OpenEvent failed,error code: %d", err);
return -1;
}
hEventClient = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("shareEventClient"));
if (!hEventClient)
{
int err = GetLastError();
printf("OpenEvent failed,error code: %d", err);
return -1;
}
while (WAIT_OBJECT_0 == WaitForSingleObject(hEventClient, INFINITE)) //等待客户端可读,阻塞
{
char p[1024];
memcpy(p, lpMapView, sizeof(p)); //将内存1024字节全部拷贝出来,只是拷贝,内存仍有数据
cout << "read from share memory: " << p << endl;
ResetEvent(hEventClient); //将客户端读重置为false
SetEvent(hEventSrv); //将服务写置为true
Sleep(1500);
}
}
}
else
{
// TODO: 更改错误代码以符合您的需要
wprintf(L"错误: GetModuleHandle 失败\n");
nRetCode = 1;
}
return nRetCode;
}
同/异步
总结一下小编所熟悉的多进程同/异步控制方法:
- 互斥量
- 信号量
- 事件
- atomic原子操作(pv操作)
更多推荐
已为社区贡献1条内容
所有评论(0)