Windows 共享内存实现进程间通讯

  • Post author:
  • Post category:其他




Windows共享内存



windows



使用

CreateFileMappingW

,

MapViewOfFile

函数实现共享内存的创建和挂载。

使用了

CreateEvent

,

CreateMutex

函数实现进程间的互斥通讯




创建共享内存

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

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

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

可以获取对应的共享内存指针

具体介绍:

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

具体介绍:

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

HANDLE pMutex = OpenMutexW(NULL, FALSE, lockname);
if (m_pMutex == nullptr)
{
	std::cerr << "CreateMutex:> Error number is " << GetLastError() << std::endl;
	return nullptr;
}



加锁

WaitForSingleObject

等待某个事件的发生,这里用来等待并拿取互斥锁的权限

具体介绍:

MSDN WaitForSingleObject

WaitForSingleObject(pMutex, INFINITE);


INFINITE

表示无限等待时长




解锁

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

具体介绍:

MSDN SetEvent


就是使另一进程阻塞在

WaitForSingleObject

函数的进程继续执行

SetEvent(pEvent);



重置事件对象

ResetEvent

具体介绍:

MSDN ResetEvent


重置之后通知才可以被再次触发

ResetEvent(pEvent);




以上是所有需要用的函数以及简单使用方式



接下来是具体代码




1

ShareManage.h

#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

#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

#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 主进程(先开起的进程)

等待其他进程写入数据并读取输出

#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 (后开启进程)

向主进程写数据

#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

代码具体如何拷贝运行这里就不过多赘述了,都学到共享内存了不应该不会吧。。。。



版权声明:本文为hh1226968736原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。