共享内存
一 进程逻辑空间 物理空间
如上图所示,每个进程都有自己的逻辑空间,这些逻辑空间,会被映射到具体的物理空间中。
每个进程的逻辑空间都是彼此隔离,相互独立不受干扰的。
但是他们都会被映射到同一个物理空间去,当其所映射的物理空间有重叠的时,这重叠的部分就共享了物理内存
二 共享内存关键问题
创建物理内存
将物理内存映射到进程空间
读写互斥控制
1)创建物理内存,获得物理内存资源句柄
比如 由A进程 负责创建共享内存
A: 进程创建完物理内存后,直接返回物理内存句柄
// 创建共享文件句柄
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // 物理文件句柄
NULL, // 默认安全级别
PAGE_READWRITE, // 可读可写
0, // 高位文件大小
BUF_SIZE, // 地位文件大小
L"ShareMemory" // 共享内存名称
);
B:B进程通过共享内存的全局名字,来获得共享物理内存句柄
// 打开共享的文件对象
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,L"ShareMemory");
2) 物理内存映射进程空间
各个进程都是通过这种方式, 将物理空间共享内存,映射到进程逻辑空间
// 映射缓存区视图 , 得到指向共享内存的指针
LPVOID lpBase = MapViewOfFile(
hMapFile, // 共享内存的句柄
FILE_MAP_ALL_ACCESS, // 可读写许可
0,
0,
BUF_SIZE
);
3) 释放
进程退出时,要解除映射
// 解除文件映射
UnmapViewOfFile(lpBase);
// 关闭内存映射文件对象句柄
CloseHandle(hMapFile);
4)互斥控制
采用互斥量或信号的机制
互斥量机制
进程A:
创建互斥量mutex
while(1)
{
申请mutex;
处理C;
释放mutex;
其他操作;
}
进程B:
while(1)
{
申请mutex;
处理C;
释放mutex;
其他操作;
}
示例:
进程A
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
// 若不存在名为"pmutex"的互斥量则创建它;否则获取其句柄
HANDLE hMutex = CreateMutex(NULL, false, "pmutex");
if(NULL == hMutex)
{
cout<<"create mutex error "<<GetLastError()<<endl;
return 0;
}
else
{
cout<<" create mutex success:"<<hMutex<<endl;
}
for(int i = 0;i<10; i++)
{
// 申请对互斥量的占有
DWORD d = WaitForSingleObject(hMutex, INFINITE);
if(WAIT_OBJECT_0 == d)
{
// 模拟对公共内存/文件的操作
cout<<"begin sleep"<<endl;
Sleep(2000);
cout<<"process 1"<<endl;
// 操作完毕,释放对互斥量的占有
if(ReleaseMutex(hMutex)!=0)
{
cout<<"reslease ok"<<endl;
}
else
{
cout<<"reslease failed"<<endl;
}
}
if(WAIT_ABANDONED == d)
{
cout<<"WAIT_ABANDONED"<<endl;
}
if(WAIT_FAILED ==d)
{
cout<<"mutex error"<<endl;
}
Sleep(2000);
}
// 释放互斥量
CloseHandle(hMutex);
hMutex = NULL;
return 0;
}
进程B
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
// 若不存在名为"pmutex"的互斥量则创建它;否则获取其句柄
HANDLE hMutex = CreateMutex(NULL, false, "pmutex");
if(NULL == hMutex)
{
cout<<"create mutex error "<<GetLastError()<<endl;
return 0;
}
else
{
cout<<" create mutex success:"<<hMutex<<endl;
}
for(int i = 0;i<10; i++)
{
// 申请对互斥量的占有
DWORD d = WaitForSingleObject(hMutex, INFINITE);
if(WAIT_OBJECT_0 == d)
{
// 模拟对公共内存/文件的操作
cout<<"begin sleep"<<endl;
Sleep(2000);
cout<<"process 2"<<endl;
// 操作完毕,释放对互斥量的占有
if(ReleaseMutex(hMutex)!=0)
{
cout<<"reslease ok"<<endl;
}
else
{
cout<<"reslease failed"<<endl;
}
}
if(WAIT_ABANDONED == d)
{
cout<<"WAIT_ABANDONED"<<endl;
}
if(WAIT_FAILED ==d)
{
cout<<"mutex error"<<endl;
}
Sleep(2000);
}
// 释放互斥量
// CloseHandle(hMutex); 互斥量的申请和释放应该由一个进程单独完成 此处有A完成,否则可能会堵塞
hMutex = NULL;
return 0;
}
信号量机制:
进程A : 执行 写操作
进程A 执行写操作
//等待其他进程读操作完毕
WaitForSingleObject(m_memMng.m_hReadEvent, INFINITE);
//重置写操作信号量
ResetEvent(m_memMng.m_hWriteEvent);
//执行写操作
memcpy
//写操作完毕,恢复信号量,使得其它进程可读
SetEvent(m_memMng.m_hWriteEvent);
进程B: 执行读操作
//进程B执行读操作
//等待主进程写完毕
WaitForSingleObject(m_memMng.m_hWriteEvent, INFINITE);
//重置读信号量
ResetEvent(m_memMng.m_hReadEvent);
//读操作,将共享内存数据拷贝本地变量
memcpy
//读操作完毕,设置信号量信号
SetEvent(m_memMng.m_hReadEvent);
三 封装
应该有的成员变量:
1)创建物理内存、映射进程空间 管理
HANDLE hMapping_; 物理空间资源句柄
MappedView View_; 物理映射成进程空间的操作
int size_; 物理空间大小
std::wstring name_; 共享内存名字
2) 信号量控制管理
HANDLE m_hReadEvent;
HANDLE m_hWriteEvent;
操作方法:
1)创建
bool CreateFileMap(); //创建
bool OpenFileMap(); //已存在,则打开
bool MapView(bool bCanWrite = true); //映射进程空间
2)映射管理类
class MappedView
{
public:
MappedView() : view_(NULL) {}
~MappedView() { CloseView(); }
bool MapView(SharedMemory *shared_memory, bool can_write);//MapViewOfFile
void CloseView();
char *view() { return view_; }
private:
char *view_=nullptr;
};
共享内存管理类
class SharedMemory
{
public:
SharedMemory() : hMapping_(NULL) {
size_ = 0;
m_hReadEvent = NULL;
m_hWriteEvent = NULL;
}
~SharedMemory() {
Close();
if (m_hReadEvent)
{
CloseHandle(m_hReadEvent);
}
if (m_hWriteEvent)
CloseHandle(m_hWriteEvent);
};
public:
//初始化
bool InitReceiver(std::wstring name, std::wstring readName = L"ShareMemoryReadEvent", std::wstring writeName = L"ShareMemoryWriteEvent");//初始化接收方
bool InitCreate(std::wstring name, int size,std::wstring readName=L"ShareMemoryReadEvent",std::wstring writeName=L"ShareMemoryWriteEvent"); //初始化创建方
private:
bool CreateFileMap();
bool OpenFileMap();
bool MapView(bool bCanWrite = true);
void Close();
public:
char* getData();
int getSize()
{
return size_;
}
public:
//信号量管理
void WaitForSingleObject_Read();
void WaitForSingleObject_Write();
void ResetEventNull_Read();
void ResetEventNull_Write();
void SetEvent_Read();
void SetEvent_Write();
private:
friend class MappedView;
MappedView View_;
HANDLE hMapping_=nullptr;
int size_=0;
std::wstring name_;
public:
HANDLE m_hReadEvent=nullptr;
HANDLE m_hWriteEvent=nullptr;
std::wstring m_ReadEventName; // 跨进程信号量 以 L"Global\\" 开头
std::wstring m_WriteEventName;
};
应用实例:
主进程发送摄像头数据
主进程
SharedMemory m_memMng;
m_memMng.InitCreate(L"sharedCamera", 1920 * 1080 * 3 / 2 + 12);//0|1状态 +宽+高
////////////////////////////////////////////////////////////////
char* pData = m_memMng.getData();
INT32 nFps = g_pLiveForm->m_videoFps;
INT32 nWidth = width;
INT32 nHeight = height;
INT32 Info[] = { nFps, nWidth, nHeight };
int len = sizeof(Info) / sizeof(Info[0]);
if (pData)
{
m_memMng.WaitForSingleObject_Read();
m_memMng.ResetEventNull_Write();
memcpy((INT32*)pData, Info, sizeof(INT32)*len);
memcpy(pData + 12, nim_nls::LssManange::GetVideoFrameMng()->capture_video_pic_.pdata_, nim_nls::LssManange::GetVideoFrameMng()->capture_video_pic_.size_);
m_memMng.SetEvent_Write();
}
子进程读摄像头数据
SharedMemory m_memMng;
m_memMng.InitReceiver(L"sharedCamera");
//读取
m_memMng.WaitForSingleObject_Write(); // WaitForSingleObject(m_memMng.m_hWriteEvent, INFINITE);
m_memMng.ResetEventNull_Read(); // ResetEvent(m_memMng.m_hReadEvent);
char* pData = m_memMng.getData();
if (pData)
{
INT32 nFps = 20;
INT32 nWidth = width_;
INT32 nHeight = height_;
INT32 Info[] = { 0, nWidth, nHeight };
int len = sizeof(Info) / sizeof(Info[0]);
memcpy(Info, (INT32*)pData, sizeof(INT32)*len);
nFps = Info[0];
nWidth = Info[1];
nHeight = Info[2];
memcpy((char*)data_temp.c_str(), pData + 12, nWidth*nHeight * 3 / 2);
m_memMng.SetEvent_Read(); //SetEvent(m_memMng.m_hReadEvent);
}