windows上共享內存操作

windows共享內存

共享內存主要是通過映射機制實現的。windows下進程的地址空間在邏輯上是相互隔離的,但是在物理上確實相互重疊的。所謂的重疊是指同一塊內存區域可能被多個進程同時使用。
在windows程序開發過程中,當多個進程之間需要訪問共同的數據的時候,最好的方式就是使用共享內存進行處理。
編寫共享內存的程序模式上基本上是一樣的,大致爲下面幾個過程:

  1. 調用CreateFileMapping創建指定命名的內存映射文件對象,此時windows即在物理內存中申請一塊指定大小的內存區域,並返回文件映射對象的句柄。
  2. 爲了訪問這塊內存中,必須調用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,用來說明共享內存可讀還是可寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章