C++实现进程间通信介绍及共享内存实例分析

        进程间通信有多种方式,这篇博客简单介绍,并且会详细介绍共享内存方式的进程间通信,其四要素为:设定共享内存区域、找出共享内存、同步处理、清理。本篇博客提供一个例子,详细使用参见这两篇博客:用法参考1用法参考2

进程间通信的方式大致有:

        1.匿名管道,底层通过网络实现通信,提供字节模式和消息模式

        2.消息管道,使用Windows消息,一段发送,一段接受

        3.套接字,Socket网络编程

        4.剪切板,CWnd提供支持

        5.动态数据交换

        6.邮件槽,基于广播通信体系,是一种单向通信机制

        7.共享内存,这种方法建立一片共享内存区,两个程序都可以访问这篇区域

此外,还有其它方式,接下来会详细介绍共享内存方式的进程间通信。

1.在程序A中,创建一片内存共享区域,并设置同步变量

         这里注意,指定了内存区域的大小

HANDLE m_hFileMapping;
LPTSTR p;
/////////////////////
void CbaseDlg::InitInterprocess()
{
	m_hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 2048*4096*4, _T("BaseImage"));
	m_hMutex = CreateEvent(nullptr, false, false, _T("BaseImageMutex"));
        p = (LPTSTR) MapViewOfFile(m_hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

//	WaitForSingleObject(m_hMutex, 0);	// in the situation that algorithm running before wait after createmutex
										// the algorithm would achieve a frame of error image
}

2.在程序B中,可以通过设置的同步量抓取数据

	FetchImage::FetchImage() :
		buffer_(nullptr),
		fileMapping_(nullptr)
	{
		fileName_ = new char[30];

		memcpy(fileName_, "BaseImage", 10);

		mutex_ = OpenEvent(MUTEX_ALL_ACCESS, false, "BaseImageMutex");

		fileMapping_ = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, fileName_);

                pMsg = (LPTSTR)MapViewOfFile(fileMapping_, FILE_MAP_ALL_ACCESS, 0, 0, 0);

	}

3.当要发送数据时,程序A中写入共享内存区域,同时要保证同步

        将要传送的数据写入共享内存区,给B程序抓取

        第二段代码为A程序创建B程序的进行,如果B进程还未被创建,CreateProcess 就根据可执行文件名创建了一个进程,这不是必须的

			m_bCapture = true;	// communicate inter process
			SendInterprocess();
/////////////////////////////////////////////////////
void CbaseDlg::SendInterprocess()
{
        LPTSTR p = m_pMapping;
	if (m_bCapture == true)
	{
		m_bCapture = false;

	//	WaitForSingleObject(m_hMutex, INFINITE);	// not need to lock

		
		((int*)p)[0] = m_image.m_imgLength;
		((int*)p)[1] = m_image.m_imgWidth;
		((int*)p)[2] = m_image.m_imgHeight;
		((int*)p)[3] = m_image.m_nBitCount;
		memcpy(((char*)p) + 16, m_image.m_pImgData, m_image.m_imgLength);

		CString str;
		GetDlgItemText(IDC_EDIT_EXE_FILENAME, str);

		if (str.GetLength() != 0)
		{
			// test whether the thread is running
			DWORD   ExitCode;
			GetExitCodeProcess(m_pInfo.hProcess,&ExitCode);
			if( ExitCode != STILL_ACTIVE )
			{
				STARTUPINFO si = { sizeof(si) };  			
			
				si.dwFlags = STARTF_USESHOWWINDOW;  // 指定wShowWindow成员有效  
				si.wShowWindow = TRUE;          // 此成员设为TRUE的话则显示新建进程的主窗口,  
				// 为FALSE的话则不显示  
				BOOL bRet = ::CreateProcess (  
					str,           // 不在此指定可执行文件的文件名  
					nullptr,      // 命令行参数  
					NULL,           // 默认进程安全性  
					NULL,           // 默认线程安全性  
					FALSE,          // 指定当前进程内的句柄不可以被子进程继承  
					CREATE_NEW_CONSOLE, // 为新进程创建一个新的控制台窗口  
					NULL,           // 使用本进程的环境变量  
					NULL,           // 使用本进程的驱动器和目录  
					&si,  
					&m_pInfo);  

			}
		}

		SetEvent(m_hMutex);	// set the event for alg
	}
}

4.B程序根据同步时间,来抓取数据

        读取时,首先锁住共享内存区域,然后使用MapViewOfFile获取共享内存区的首地址,然后取出数据即可

        在A中,SetEvent触发事件,在这里,ResetEvent进行复位

        LPTSTR pMsg;
	unsigned char* FetchImage::getImage()
	{
		if (mutex_ == nullptr || fileMapping_ == nullptr)
		{
			throw "base工程未启动";
			return nullptr;
		}

		WaitForSingleObject(mutex_, INFINITE);	// lock filemapping

		

		if (imageSize_ < ((int*)pMsg)[0])	// buffer size is not enough
		{
			delete buffer_;
			buffer_ = nullptr;
		}
		imageSize_ = ((int*)pMsg)[0];
		if (buffer_ == nullptr)
		{
			buffer_ = new unsigned char[imageSize_ + HEAD_SIZE];
		}

		memcpy(buffer_, pMsg, imageSize_ + HEAD_SIZE);	// save the data

		ResetEvent(mutex_);		// unlock

		// ((int*)buffer_)[0] is image length (byte)
		imageWidth_ = ((int*)buffer_)[1];
		imageHeight_ = ((int*)buffer_)[2];
		imageType_ = ((int*)buffer_)[3];

		return buffer_ + HEAD_SIZE;	// return head of image data 
	}

5.清理

        在A程序和B程序中都要进行清理。

FetchImage::~FetchImage()
	{
		if (!UnmapViewOfFile(pMsg_)){
			//assert(false);
		}
		CloseHandle(fileMapping_);
		CloseHandle(mutex_);

		delete fileName_;
		delete buffer_;
	}

 

        特别说明,每调用一次MapViewOfFile,就会分配一片与共享内存大小相同的内存区域,一般一个进程中只用调用一次,如果重复调用会出现内存不足。

        OK,See You Next Chapter!

发表评论