Windows下基于共享内存实现进程间通信
案例场景描述:
- 进程A申请共享内存,同时创建互斥量,取得互斥量的所有权后,向共享内存中写入数据,之后释放互斥量
- 进程B打开共享内存,请求互斥量的所有权,读取共享内存数据,之后释放互斥量
进程A代码
基于共享内存的方式实现进程间通信在效率上具有明显的优势,代码也非常简洁。
对于需要传递大量数据的场景来说,可以将共享内存的地址空间进行强制类型转换,结构体是一种非常好的选择。
但需要注意的是,这种方式只可以是C风格的类型,因为强制类型转换不会调用构造函数。
如果在SharedMemoryContents
这个结构体中加入std::list成员,可能会引起崩溃,因为它无法被正确初始化。
//------------------------进程A初始化------------------------------
const unsigned long BUF_SIZE = sizeof(SharedMemContents);
bool valid = true; //校验进程A的初始化是否正确执行
//创建共享文件句柄,具有读写权限
HANDLE sharedMemHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, BUF_SIZE, L"SharedMemory");
if (sharedMemHandle == nullptr)
{
valid = false;
return;
}
//将共享内存的全部空间映射为自定义的结构体类型
SharedMemContents *pcontents = (SharedMemContents*)MapViewOfFile(sharedMemHandle, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
if (pcontents == nullptr)
{
valid = false;
return;
}
//清理pcontents的内容
pcontents->tableRows = 0;
memset(pcontents->otherTitle, 0, 1024 * 5);
memset(pcontents->tableTitles, 0, 1024 * 5);
//创建互斥量,如果bInitialOwner设为true,会导致后面即使ReleaseMutex后,进程B也无法获得所有权,未明原因?
HANDLE mutexHandle = CreateMutex(nullptr, false, L"SharedMemory_mutex");
if (mutexHandle == nullptr)
{
valid = false;
return;
}
//------------------------进程A将数据写入共享内存------------------------------
//获取互斥量的所有权
DWORD wait_res = WaitForSingleObject(mutexHandle, INFINITE);
switch (wait_res)
{
case WAIT_ABANDONED:
valid = false;
return;
case WAIT_OBJECT_0:
memcpy(pcontents->otherTitle, other_title.c_str(), other_title.size());
memcpy(pcontents->tableTitles, table_title.c_str(), table_title.size());
ReleaseMutex(mutexHandle);
}
//------------------------进程A结束前的清理工作------------------------------
ReleaseMutex(mutexHandle);
CloseHandle(mutexHandle);
UnmapViewOfFile(pcontents);
CloseHandle(sharedMemHandle);
pcontents = nullptr;
进程B代码
//------------------------进程B初始化-------------------------
//打开共享内存,只有读权限
HANDLE shm_handle = OpenFileMapping(FILE_MAP_READ, false, L"SharedMemory");
if (!shm_handle)
{
return -1;
}
//获取共享内存的地址,并将其全部空间映射为结构体类型
SharedMemContents *pcontents = (SharedMemContents*)MapViewOfFile(
shm_handle, FILE_MAP_READ, 0, 0, sizeof(SharedMemContents));
if (!pcontents)
{
CloseHandle(shm_handle);
return -1;
}
//打开互斥量,这里其实使用CreateMutex也是一样的,只要同名,仍然可以获得同一个互斥量的句柄
HANDLE mutex_handle = OpenMutex(MUTEX_ALL_ACCESS, false, L"SharedMemory_mutex");
if (!mutex_handle)
{
UnmapViewOfFile(pcontents);
CloseHandle(shm_handle);
return -1;
}
//------------------------进程B读取共享内存数据-------------------------
while (true)
{
//请求互斥量的所有权,保持等待
DWORD wait_res = WaitForSingleObject(mutex_handle, INFINITE);
switch (wait_res)
{
case WAIT_OBJECT_0:
{
//获取互斥量的所有权
std::cout << pcontents->otherTitle << "\n"
<< pcontents->tableTitles << "\n";
for (int i = 0; i < pcontents->tableRows; ++i)
{
for (int j = 0; j < 21; ++j)
{
std::cout << pcontents->tableValues[i * 21 + j] << ",";
}
std::cout << "\n";
}
ReleaseMutex(mutex_handle);
break;
}
case WAIT_ABANDONED:
//TODO 这里的设计有缺陷,
//在进程退出时,如果没有ReleaseMutex,等待的结果才会是WAIT_ABANDONED
//而进程A退出时,主动释放了互斥量,把return放在这里,可能会陷入死循环
ReleaseMutex(mutex_handle);
CloseHandle(mutex_handle);
UnmapViewOfFile(pcontents);
CloseHandle(shm_handle);
return true;
}
}