windows共享内存
共享内存主要是通过映射机制实现的。windows下进程的地址空间在逻辑上是相互隔离的,但是在物理上确实相互重叠的。所谓的重叠是指同一块内存区域可能被多个进程同时使用。
在windows程序开发过程中,当多个进程之间需要访问共同的数据的时候,最好的方式就是使用共享内存进行处理。
编写共享内存的程序模式上基本上是一样的,大致为下面几个过程:
- 调用CreateFileMapping创建指定命名的内存映射文件对象,此时windows即在物理内存中申请一块指定大小的内存区域,并返回文件映射对象的句柄。
- 为了访问这块内存中,必须调用MapViewOfFile(),促使windows将句柄hMap指向的那一块内存区域映射到进程的地址空间中。MapViewOfFile()返回指向该内存空间的指针。
下面是两个共享内存的示例。
示例1:使用共享内存在两个进程之间传输字符串。
#include <Windows.h>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main()
{
string strMapName("ShareMemory");
string strComData("This is common data");
LPVOID pBuffer; //LPVOID其实就是void*,也就意味着任意类型的地址都可以间接引用这个指针
//打开一个命名为"ShareMemory"的内存映射文件对象
HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, (LPCWSTR)strMapName.c_str());
if (NULL == hMap)
{
//打开失败,则创建该内存映射文件对象
hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
strComData.length() + 1,
(LPCWSTR)strMapName.c_str());
//将该内存空间映射到进程的地址空间中
pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
//向内存中写入数据
strcpy((char*)pBuffer, strComData.c_str());
cout << "写入共享内存数据:" << (char*)pBuffer << endl;
}
else
{
//打开成功,直接将该内存空间映射到进程的地址空间中
pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
cout << "读取共享内存数据:" << (char*)pBuffer << endl;
}
getchar();
//解除内存空间映射
::UnmapViewOfFile(pBuffer);
//关闭内存映射文件对象句柄
::CloseHandle(hMap);
system("pause");
return 0;
}
示例2:使用共享内存在两个进程之间共享图片。
ShareMemory.h
#ifndef _SHAREMEMORY_H
#define _SHAREMEMORY_H
#include <opencv2\opencv.hpp>
#include <Windows.h>
#include <string.h>
using namespace cv;
using namespace std;
extern LPVOID pBuffer;
//Mat头部
typedef struct
{
int width;
int height;
int type;
}MatHeader;
//各个块的大小
#define FLAG_SIZE sizeof(char)
#define MAT_HEADER_SIZE sizeof(MatHeader)
#define MAT_SIZE 1920*1080*3*8
#define MEMORY_SIZE FLAG_SIZE + MAT_HEADER_SIZE + MAT_SIZE
//内存首地址
#define FLAG_ADDRESS (char*)pBuffer
#define MAT_HEADER_ADDRESS FLAG_ADDRESS + FLAG_SIZE
#define MAT_DATA_ADDRESS MAT_HEADER_ADDRESS + MAT_HEADER_SIZE
//func
bool initShareMemory();
void writeFlag2ShareMemory(char* FLAG_Address, char value);
void readFlagFromShareMemory(char* FLAG_Address, char& value);
void writeMat2ShareMemory(char* MatHeadAddress, char* MatDataAddress, Mat image);
void ReadMatFromShareMemory(char* MatHeadAddress, char* MatDataAddress, Mat& image);
#endif // !_SHAREMEMORY_H
ShareMemory.cpp
#include "ShareMemory.h"
HANDLE hMapFile;
LPVOID pBuffer;
bool initShareMemory()
{
string memeryName = "SharePicture";
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
MEMORY_SIZE,
(LPCWSTR)memeryName.c_str()
);
if (hMapFile == NULL)
{
return false;
}
pBuffer = (LPTSTR)MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
MEMORY_SIZE
);
if (pBuffer == NULL)
{
CloseHandle(hMapFile);
return false;
}
//初始化共享内存内容全是0
memset((char*)pBuffer, 0, MEMORY_SIZE);
return true;
}
void writeFlag2ShareMemory(char * FLAG_Address, char value)
{
memcpy(FLAG_ADDRESS, &value, sizeof(value));
}
void readFlagFromShareMemory(char * FLAG_Address, char& value)
{
memcpy(&value, FLAG_Address, sizeof(value));
}
void writeMat2ShareMemory(char * MatHeadAddress, char * MatDataAddress, Mat image)
{
MatHeader matHead;
matHead.width = image.cols;
matHead.height = image.rows;
matHead.type = image.type();
//写入信息头到内存中
memcpy(MatHeadAddress, &matHead, sizeof(MatHeader));
//根据Mat type()对8的余数计算Mat元素所占的字节数
//https://www.jianshu.com/p/204f292937bb
int ByteNum = 1;
switch (image.type() % 8)
{
case 0:
case 1:
ByteNum = 1;
break;
case 2:
case 3:
ByteNum = 2;
break;
case 4:
case 5:
ByteNum = 4;
break;
case 6:
ByteNum = 8;
break;
}
int write_bits = 0;
for (int row = 0; row < image.rows; ++row)
{
write_bits = row * image.cols * image.channels() * ByteNum;
memcpy(MatDataAddress + write_bits, image.ptr(row), image.cols * image.channels() * ByteNum);
}
}
void ReadMatFromShareMemory(char * MatHeadAddress, char * MatDataAddress, Mat & image)
{
//读入信息头
MatHeader matHead;
memcpy(&matHead, MatHeadAddress, sizeof(MatHeader));
//根据Mat type()对8的余数计算Mat元素所占的字节数
//https://www.jianshu.com/p/204f292937bb
int ByteNum = 1;
switch (matHead.type % 8)
{
case 0:
case 1:
ByteNum = 1;
break;
case 2:
case 3:
ByteNum = 2;
break;
case 4:
case 5:
ByteNum = 4;
break;
case 6:
ByteNum = 8;
break;
}
//创建image
image.create(matHead.height, matHead.width, matHead.type);
//从内存中copy数据
int write_bits = 0;
for (int row = 0; row < matHead.height; ++row)
{
write_bits = row * image.cols * image.channels() * ByteNum;
memcpy(image.ptr(row), MatDataAddress + write_bits, image.cols * image.channels() * ByteNum);
}
}
main1.cpp发图片
#include "ShareMemory.h"
int main()
{
string dir = "..\\images\\img_560";
vector<cv::String> img_names;
cv::glob(dir, img_names);
initShareMemory();
for (int i = 0; i < img_names.size(); )
{
char flag;
readFlagFromShareMemory(FLAG_ADDRESS, flag);
if (0 == flag)
{
Mat img = imread(img_names[i]);
imshow("img", img);
waitKey(1);
writeMat2ShareMemory(MAT_HEADER_ADDRESS, MAT_DATA_ADDRESS, img);
writeFlag2ShareMemory(FLAG_ADDRESS, 1);
++i;
}
Sleep(10);
}
return 0;
}
main2.cpp接收图片
#include "ShareMemory.h"
int main()
{
initShareMemory();
while (true)
{
char flag;
readFlagFromShareMemory(FLAG_ADDRESS, flag);
if (1 == flag)
{
cv::Mat image;
ReadMatFromShareMemory(MAT_HEADER_ADDRESS, MAT_DATA_ADDRESS, image);
cv::imshow("img", image);
cv::waitKey(1);
writeFlag2ShareMemory(FLAG_ADDRESS, 0);
}
else
{
cout << "共享内存为空..." << endl;
}
}
}
说明:共享内存没有任何的同步机制来控制多个进程对共享内存空间的同步访问,因此为了确保数据的一致性,上面的程序中设置了一个标志位flag,用来说明共享内存可读还是可写。