Windows 共享内存实现进程间通讯
Windows共享内存
在
windows
中
使用
CreateFileMappingW
,
MapViewOfFile
函数实现共享内存的创建和挂载。
使用了
CreateEvent
,
CreateMutex
函数实现进程间的互斥通讯
创建共享内存
CreateFileMapping
CreateFileMapping
具体介绍:
MSDN CreateFileMappingW
具体介绍可以看上方的官方文档这里不过多赘述,不过有一点需要提一下。
CreateFileMapping函数有两种
1.CreateFileMappingW
2.CreateFileMappingA
查了一下资料,好像只有最后一个参数不一样,一个是
w_char*
的宽字符类型, 一个是
char*
的类型, Windows api 的底层调用好像都是
w_char*
的,
CreateFileMappingA
函数只是对字符类型做了一个转换再调用
CreateFileMappingW
,具体的我也没有仔细了解。
string 转 w_char*
LPCWSTR stringToLPCWSTR(const char* orig)
{
size_t origsize = strlen(orig) + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (strlen(orig) - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);
return wcstring;
}
下文参数默认为 w_char*
创建一个
size
大小的命名共享内存对象,名称为
filename
size
filename
HANDLE hMapFile = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, size, filename);
if (m_hMapFile == nullptr)
{
std::cerr << "Failed to create file mapping, error number is " << GetLastError() << std::endl;
return false;
}
其他进程打开共享内存
OpenFileMappingW
OpenFileMappingW
HANDLE hMapFile = OpenFileMappingW(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, size, filename);
if (m_hMapFile == nullptr)
{
std::cerr << "Failed to create file mapping, error number is " << GetLastError() << std::endl;
return false;
}
注意在程序使用期间 hMapFile 句柄不能关闭,否则会导致其他进程无法访问共享内存
挂载共享内存
MapViewOfFile
MapViewOfFile
使用
MapViewOfFile
可以获取对应的共享内存指针
具体介绍:
MSDN MapViewOfFile
void* startMem = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, size);
if (m_startMam == nullptr)
{
std::cerr << "Failed to map view of file, error number is " << GetLastError() << std::endl;
return false;
}
windows进程间互斥
创建互斥对象
CreateMutex
CreateMutex
具体介绍:
MSDN CreateMutex
创建一个名为
lockname
的命名锁对象
HANDLE pMutex = CreateMutexW(NULL, FALSE, lockname);
if (m_pMutex == nullptr)
{
std::cerr << "CreateMutex:> Error number is " << GetLastError() << std::endl;
return nullptr;
}
打开互斥锁对象
OpenMutex
OpenMutex
HANDLE pMutex = OpenMutexW(NULL, FALSE, lockname);
if (m_pMutex == nullptr)
{
std::cerr << "CreateMutex:> Error number is " << GetLastError() << std::endl;
return nullptr;
}
加锁
WaitForSingleObject
WaitForSingleObject
等待某个事件的发生,这里用来等待并拿取互斥锁的权限
具体介绍:
MSDN WaitForSingleObject
WaitForSingleObject(pMutex, INFINITE);
INFINITE
表示无限等待时长
解锁
ReleaseMutex
ReleaseMutex
调用进程释放互斥对象的所有权
具体介绍:
MSDN ReleaseMutex
ReleaseMutex(pMutex);
Windows 进程间同步
创建 / 获取 事件对象 CreateEvent
创建或打开命名或未命名的事件对象,如果不存在创建事件对象,如果存在获取对象
具体介绍:
MSDN CreateEvent
pEvent = CreateEventW(NULL, TRUE, FALSE, eventname);
if (pEvent == nullptr)
{
std::cerr << "CreateEvent:> Error number is " << GetLastError() << std::endl;
return nullptr;
}
等待事件对象通知
与等待互斥锁一样使用
WaitForSignalObject
函数
WaitForSingleObject(pEvent, INFINITE);
发送事件通知
SetEvent
SetEvent
具体介绍:
MSDN SetEvent
就是使另一进程阻塞在
WaitForSingleObject
函数的进程继续执行
SetEvent(pEvent);
重置事件对象
ResetEvent
ResetEvent
具体介绍:
MSDN ResetEvent
重置之后通知才可以被再次触发
ResetEvent(pEvent);
以上是所有需要用的函数以及简单使用方式
接下来是具体代码
1
ShareManage.h
1
#ifndef _SHARE_
#define _SHARE_
#include <Windows.h>
#include <string.h>
#include <thread>
#include <list>
#include <iostream>
#include <string>
#include <cstring>
using std::list;
using std::thread;
using std::iostream;
using std::string;
using std::memset;
struct Interaction
{
char requestApp[20];
size_t datalen;
};
class ShareManage
{
public:
ShareManage(string fileName, size_t size, bool isMain);
~ShareManage();
public:
//初始化/创建共享内存
bool ShareMemaryInit();
//获取进程间互斥锁
HANDLE GetProcMutex();
//获取进程间事件对象
HANDLE GetProcEvent();
//获取数据空间指针
inline void* GetMemary() {
return m_memary;
}
private:
//初始化进程间互斥锁以及条件变量
bool MutexAndCondInit();
private:
HANDLE m_pMutex; //共享内存互斥锁
HANDLE m_pEvent; //事件对象
HANDLE m_hMapFile; //映射文件对象句柄
void* m_startMam; //共享内存起始地址指针
void* m_memary; //共享内存中数据指针
bool m_isMain; //是否为主进程,主进程应初始化互斥量
public:
size_t m_size; //共享内存大小
size_t m_memSize; //共享内存数据块大小
string m_fileName; //共享内存映射文件名
};
#endif // _SHARE_
2
ShareManage.cpp
2
#include "ShareManage.h"
LPCWSTR stringToLPCWSTR(const char* orig)
{
size_t origsize = strlen(orig) + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (strlen(orig) - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);
return wcstring;
}
ShareManage::ShareManage(string fileName, size_t size, bool isMain)
: m_fileName(fileName), m_size(size), m_isMain(isMain)
{
m_memary = nullptr;
m_pMutex = nullptr;
m_startMam = nullptr;
m_pEvent = nullptr;
m_hMapFile = nullptr;
m_memSize = m_size;
}
bool ShareManage::ShareMemaryInit()
{
string name = m_fileName + ".mem";
LPCWSTR filename = stringToLPCWSTR(name.c_str());
if (m_isMain)
m_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, m_size, filename);
else
m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, filename);
if (m_hMapFile == nullptr)
{
std::cerr << "Failed to create file mapping, error number is " << GetLastError() << std::endl;
return false;
}
m_startMam = MapViewOfFile(m_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, m_size);
if (m_startMam == nullptr)
{
std::cerr << "Failed to map view of file, error number is " << GetLastError() << std::endl;
return false;
}
return MutexAndCondInit();
}
bool ShareManage::MutexAndCondInit()
{
if (m_startMam == nullptr) return false;
char* auxMemary = static_cast<char*>(m_startMam);
m_memary = auxMemary;
return true;
}
HANDLE ShareManage::GetProcMutex()
{
string name = m_fileName + ".loc";
LPCWSTR lockname = stringToLPCWSTR(name.c_str());
if (m_isMain)
m_pMutex = CreateMutex(NULL, FALSE, lockname);
else
m_pMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, lockname);
if (m_pMutex == nullptr)
{
std::cerr << "CreateMutex:> Error number is " << GetLastError() << std::endl;
return nullptr;
}
return m_pMutex;
}
HANDLE ShareManage::GetProcEvent()
{
string name = m_fileName + ".eve";
LPCWSTR eventname = stringToLPCWSTR(name.c_str());
m_pEvent = CreateEvent(NULL, TRUE, FALSE, eventname);
if (m_pEvent == nullptr)
{
std::cerr << "CreateEvent:> Error number is " << GetLastError() << std::endl;
return nullptr;
}
return m_pEvent;
}
ShareManage::~ShareManage()
{
// 解除文件映射
if (UnmapViewOfFile(m_startMam) == 0)
{
std::cerr << "UnmapViewOfFile failed:> error number is " << GetLastError() << std::endl;
}
// 关闭内存映射文件对象句柄
CloseHandle(m_hMapFile);
// 操作完成后释放进程锁
if (m_pMutex != NULL)
{
CloseHandle(m_pMutex);
}
}
以下是一个共享内存管理类,可以用类似于 list 的方式进行进程间数据交互
3
share_vector.h
3
#ifndef STATIC_VECTOR
#define STATIC_VECTOR
#include <stdlib.h>
#include <cstring>
#include <stdexcept>
using std::memcpy;
using std::memset;
constexpr size_t _tSize = sizeof(size_t);
class share_queue
{
public:
//构造函数初始化成员变量
share_queue(void* memory, size_t size, bool isMain)
{
m_totalSize = size;
m_isMain = isMain;
m_needSize = _tSize * 3;
if (m_totalSize < m_needSize)
throw std::out_of_range("The space is too small to initialize.");
if (m_isMain) memset(memory, 0, size);
m_start = static_cast<char*>(memory);
m_read = (size_t*)m_start;
m_start += _tSize;
m_write = (size_t*)m_start;
m_start += _tSize;
m_size = (size_t*)m_start;
m_start += _tSize;
m_change = (size_t*)m_start;
m_start += _tSize;
if (m_isMain)
{
*m_read = 0;
*m_write = 0;
*m_size = 0;
*m_change = 0;
}
}
~share_queue() {}
public:
//读取消息
size_t read(char* buffer)
{
if (*m_size == 0)
return (size_t)(~0);
if (*m_read == *m_change && *m_change != 0)
{
*m_read = 0;
*m_change = 0;
}
size_t moved = *m_read;
char* data = m_start + moved;
size_t datalen = *((size_t*)data);
data += _tSize;
memcpy(buffer, data, datalen);
*m_read += datalen + _tSize;
(*m_size)--;
return datalen;
}
//写入消息
bool write(char* buffer, size_t bufflen)
{
if (*m_write == *m_read && *m_size != 0) return false;
size_t rest = 0;
if (*m_write >= *m_read)
{
rest = m_totalSize - m_needSize - *m_write;
if (rest < bufflen + _tSize)
{
rest = *m_read;
if (rest < bufflen + _tSize)
return false;
*m_change = *m_write;
*m_write = 0;
}
}
else
{
rest = *m_read - *m_write;
if (rest < bufflen + _tSize)
return false;
}
size_t moved = *m_write;
char* data = m_start + moved;
memcpy(data, &bufflen, _tSize);
data += _tSize;
memcpy(data, buffer, bufflen);
*m_write += bufflen + _tSize;
(*m_size)++;
return true;
}
//清空容器
void clear()
{
memset(m_start, 0, m_totalSize - m_needSize);
*m_read = 0;
*m_write = 0;
*m_change = 0;
*m_size = 0;
}
//判断容器是否为空
inline bool empty()
{
return !(*m_size);
}
private:
size_t m_totalSize; //容器总大小
size_t m_needSize; //创建容器需要的大小
char* m_start; //起始位指针
size_t* m_read; //主线程读地址
size_t* m_write; //主线程写地址
size_t* m_change; //尾部变道标识
size_t* m_size; //容器内元素个数
bool m_isMain; //是否为主线程
};
#endif // STATIC_VECTOR
最后是测试用程序
4
TestRead.cpp 主进程(先开起的进程)
4
等待其他进程写入数据并读取输出
#include <iostream>
#include "ShareManage.h"
#include "share_queue.h"
int main()
{
ShareManage share("test", 1024, true);
if (!share.ShareMemaryInit())
{
std::cerr << "share manage initialize failed." << std::endl;
return 1;
}
HANDLE pMutex = share.GetProcMutex();
HANDLE pEvent = share.GetProcEvent();
share_queue testlist(share.GetMemary(), share.m_memSize, true);
string testmsg;
char testnum[50];
while (true)
{
while (testlist.empty())
{
ResetEvent(pEvent);
if (WaitForSingleObject(pEvent, INFINITE) != WAIT_OBJECT_0)
{
std::cerr << "WaitForSingleObject :> Wait error number is " << GetLastError() << std::endl;
}
}
DWORD ts = WaitForSingleObject(pMutex, INFINITE);
testlist.read(testnum);
ReleaseMutex(pMutex);
std::cerr << testnum;
}
return 0;
}
5
TestWrite.cpp (后开启进程)
5
向主进程写数据
#include <iostream>
#include "ShareManage.h"
#include "share_queue.h"
int main()
{
ShareManage share("test", 1024, false);
if (!share.ShareMemaryInit())
{
std::cerr << "share manage initialize failed." << std::endl;
return 1;
}
HANDLE pMutex = share.GetProcMutex();
HANDLE pEvent = share.GetProcEvent();
share_queue testlist(share.GetMemary(), share.m_memSize, false);
char text1[] = "hello world!\n";
char text2[] = "this is test message, long message....\n";
char* push = text1;
while (true)
{
DWORD ts = WaitForSingleObject(pMutex, INFINITE);
testlist.write(push, strlen(push) + 1);
ReleaseMutex(pMutex);
SetEvent(pEvent);
Sleep(1000);
if (push == text1)
push = text2;
else
push = text1;
}
return 0;
}
我的运行环境
Windows10 x64 Intel i7
Windows10 x64 Intel i7
代码具体如何拷贝运行这里就不过多赘述了,都学到共享内存了不应该不会吧。。。。