linux中进程之间通信

关于linux进程通信有很多种类实现,比如管道,队列,共享内存。说一下自己的体会

管道:其实管道操作是很简单,只需要通过pipe()函数,返回一个两个句柄,一个用来输入,一个用来输出。

例子:

INT nPipeFds[2];

	if (pipe(nPipeFds) == -1)
	{
		return FALSE;
	}

但是这种匿名管道只能够用来操作具有继承关系的进程(fork())。如果没有继承关系的进程,我选择了使用共享内存,

说一下我的思路:

1.通信双方是单向的,相当主从关系。

2.从进程开一个线程,去读取共享内存数据。

3.当共享内存中消息缓冲区满了后,主进程就不能够去读了,直接返回。


相关代码如下:


#ifndef __PROCESSCOMM__H__20141226
#define __PROCESSCOMM__H__20141226

#include "Global.h"
#include "Thread.h"
#include "ProcessCrical.h"
#include <semaphore.h>

// 单向通道进程通信
class CWriteChannel
{
public:
	BOOL CreateChannel(const CHAR *lpszChannelName, INT nMaxChannelBufSize);
	BOOL WriteData(const CHAR *lpszData, INT nDataSize);
	VOID Close();

public:
	CWriteChannel();
	virtual ~CWriteChannel();

private:
	INT m_nWriteFd;
	BYTE *m_lpbyMapAddr;
	INT m_nChannelSize;
	CProcessCrical m_pCrical; // 文件锁
	sem_t *m_pSemt;

};

struct IChannelFunc
{
	virtual VOID OnReadChannel(const CHAR *lpszChannelData, INT nDataSize) = 0;
	DISALLOW_DESTRUCT(IChannelFunc)
};

class CReadChannel: public CThread
{
public:
	BOOL OpenChannel(const CHAR *lpszChannelName, IChannelFunc *lpFunc);
	VOID Close();

public:
	CReadChannel();
	virtual ~CReadChannel();

public:
	virtual BOOL OnCreateThread(VOID *lpContext);
	virtual VOID OnRun();

private:
	INT m_nReadFd;
	CProcessCrical m_pCrical;
	IChannelFunc *m_lpChannelFunc;
	BYTE *m_lpbyMapAddr;
	INT m_nChannelBufSize;
	BOOL m_bStop;
	sem_t *m_pSemt;
};

#endif

.cpp


#include "../Include/ProcessComm.h"
#include <sys/mman.h>
#include<fcntl.h>
#include <string.h>
#include <errno.h>

#pragma pack(1)
typedef struct tagMEM_SHARE_HEADER
{
	WORD wFront;
	WORD wBack;

}MEM_SHARE_HEADER; // 用来判定缓存区区域是否满,循环队列缓冲区
#pragma pack()

#define PAGE_SIZE (4096)
#define CHANNEL_CACHE_ADDR(lpbyMapAddr) (lpbyMapAddr + sizeof(MEM_SHARE_HEADER))
#define CHANNEL_CACHE_SIZE(nSize)	(nSize - sizeof(MEM_SHARE_HEADER))

#define CHANNEL_HEADER_ADDR(lpbyMapAddr) ((MEM_SHARE_HEADER *)lpbyMapAddr)

BOOL CWriteChannel::CreateChannel(const CHAR *lpszChannelName, INT nMaxChannelBufSize)
{
	ASSERT(lpszChannelName != NULL);
	MEM_SHARE_HEADER *pMemShareHeader;
	CHAR szShareName[100];
	BOOL bSucess;

	bSucess = FALSE;
	if (nMaxChannelBufSize <= 0)
	{
		goto Exit0;
	}

	m_nWriteFd = open(lpszChannelName, O_RDWR | O_TRUNC | O_CREAT, 0644);
	if (m_nWriteFd == -1)
	{
		goto Exit0;
	}

	// 创建文件锁
	sprintf(szShareName, "%s.lck", lpszChannelName);
	if (!m_pCrical.OpenProcessCrical(szShareName))
	{
		goto Exit0;
	}

	// 创建有名信号量,用来多进程同步
	sprintf(szShareName, "%s.sem", lpszChannelName);
	m_pSemt = sem_open(szShareName, O_CREAT | O_RDWR, 0644, 0);
	if (m_pSemt == SEM_FAILED)
	{
		goto Exit0;
	}

	// mmap申请映射
	m_nChannelSize = nMaxChannelBufSize / PAGE_SIZE * PAGE_SIZE;
	if (nMaxChannelBufSize % PAGE_SIZE)
	{
		m_nChannelSize += PAGE_SIZE;
	}
	lseek(m_nWriteFd, m_nChannelSize - 1, SEEK_SET);
	write(m_nWriteFd, "", 1); // 这一句千万不能够省略

	m_lpbyMapAddr = (BYTE *)mmap(NULL, m_nChannelSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_nWriteFd, 0);
	if (m_lpbyMapAddr == (BYTE *)-1)
	{
		// 打印出出错信息
		goto Exit0;
	}

	pMemShareHeader = CHANNEL_HEADER_ADDR(m_lpbyMapAddr);
	pMemShareHeader->wBack = 0;
	pMemShareHeader->wFront = 0;

	munmap(m_lpbyMapAddr, m_nChannelSize);
	bSucess = TRUE;

Exit0:
	if (!bSucess)
	{
		if (m_nWriteFd != -1)
		{
			close(m_nWriteFd);
			m_nWriteFd = -1;
		}
		if (m_pSemt != SEM_FAILED)
		{
			sem_close(m_pSemt);
			m_pSemt = NULL;
		}
		m_pCrical.Close();
	}

	return bSucess;
}

BOOL CWriteChannel::WriteData(const CHAR *lpszData, INT nDataSize)
{
	MEM_SHARE_HEADER *pMemShareHeader;
	WORD wBackofQuque;
	BYTE *lpbyQuqueAddr;
	INT i,j;
	WORD wDataSize;

	wDataSize = nDataSize;
	m_pCrical.Lock();

	m_lpbyMapAddr 	= (BYTE *)mmap(NULL, m_nChannelSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_nWriteFd, 0);
	pMemShareHeader = CHANNEL_HEADER_ADDR(m_lpbyMapAddr);
	wBackofQuque 	= (pMemShareHeader->wBack + 2 + nDataSize) % CHANNEL_CACHE_SIZE(m_nChannelSize);
	if ((wBackofQuque + 1) == pMemShareHeader->wFront)
	{
		// 缓冲区已满
		m_pCrical.UnLock();
		return FALSE;
	}

	lpbyQuqueAddr = CHANNEL_CACHE_ADDR(m_lpbyMapAddr);
	for (i = 0; i < 2; ++ i)
	{
		lpbyQuqueAddr[pMemShareHeader->wBack ++] = *((BYTE *)&wDataSize + i);
		pMemShareHeader->wBack %= CHANNEL_CACHE_SIZE(m_nChannelSize);
	}
	for (i = 0; i < nDataSize; ++ i)
	{
		lpbyQuqueAddr[pMemShareHeader->wBack ++] = lpszData[i];
		pMemShareHeader->wBack %= CHANNEL_CACHE_SIZE(m_nChannelSize);
	}

	munmap(m_lpbyMapAddr, m_nChannelSize);
	m_pCrical.UnLock();

	// 可以开始读数据了
	sem_post(m_pSemt);

	return TRUE;
}

VOID CWriteChannel::Close()
{

}

CWriteChannel::CWriteChannel()
{
	m_nWriteFd 		= -1;
	m_lpbyMapAddr 	= NULL;
	m_nChannelSize 	= -1;
	m_pSemt			= NULL;
}


CWriteChannel::~CWriteChannel()
{

}


BOOL CReadChannel::OpenChannel(const CHAR *lpszChannelName, IChannelFunc *lpFunc)
{
	CHAR szShareName[100];
	BOOL bSucess;

	bSucess = FALSE;

	// 创建文件锁
	sprintf(szShareName, "%s.lck", lpszChannelName);
	if (!m_pCrical.OpenProcessCrical(szShareName))
	{
		goto Exit0;
	}

	// 打开sem信号量
	sprintf(szShareName, "%s.sem", lpszChannelName);
	m_pSemt = sem_open(szShareName, O_RDWR);
	if (m_pSemt == NULL)
	{
		goto Exit0;
	}

	m_nReadFd = open(lpszChannelName, O_RDWR);
	if (m_nReadFd == -1)
	{
		goto Exit0;
	}

	m_bStop = FALSE;
	bSucess = TRUE;
	m_lpChannelFunc = lpFunc;
	m_nChannelBufSize = lseek(m_nReadFd, 0, SEEK_END);
	CThread::Start(NULL);

Exit0:
	if (!bSucess)
	{
		if (m_nReadFd != -1)
		{
			close(m_nReadFd);
			m_nReadFd = -1;
		}
		if (m_pSemt != NULL)
		{
			sem_close(m_pSemt);
			m_pSemt = NULL;
		}

		m_pCrical.Close();
	}

	return bSucess;
}

VOID CReadChannel::Close()
{

}


CReadChannel::CReadChannel()
{
	m_lpChannelFunc = NULL;
	m_lpbyMapAddr = NULL;
	m_bStop = TRUE;
	m_pSemt = NULL;
}

CReadChannel::~CReadChannel()
{

}

BOOL CReadChannel::OnCreateThread(VOID *lpContext)
{
	return TRUE;
}

VOID CReadChannel::OnRun()
{
	MEM_SHARE_HEADER *pMemShareHeader;
	WORD wBack;
	WORD wFront;
	INT nOffset;
	WORD wMsgLen;
	INT nMsgBytesNum;
	BYTE *lpbyDataAddr;
	CHAR szMsgBuf[1024];

	// 使用轮xun方法
	while (!m_bStop)
	{
		sem_wait(m_pSemt);

		m_pCrical.Lock();

		// mark,这里有个问题没有说明
		m_lpbyMapAddr 	= (BYTE *)mmap(m_lpbyMapAddr, m_nChannelBufSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_nReadFd, 0);
		pMemShareHeader = CHANNEL_HEADER_ADDR(m_lpbyMapAddr);
		if (pMemShareHeader->wBack == pMemShareHeader->wFront) // 空
		{
			m_pCrical.UnLock();
			continue;
		}

		wBack = pMemShareHeader->wBack;
		wFront = pMemShareHeader->wFront;

		// 清空消息对列缓冲区
		pMemShareHeader->wBack = 0;
		pMemShareHeader->wFront = 0;
		msync(m_lpbyMapAddr, m_nChannelBufSize, MS_SYNC);

		m_pCrical.UnLock();

		// 开始解析消息
		lpbyDataAddr = CHANNEL_CACHE_ADDR(m_lpbyMapAddr);
		nOffset = 0;
		wMsgLen = 0;
		nMsgBytesNum = 0;
		while (wFront != wBack)
		{
			// 先读消息长度
			if (nMsgBytesNum < 2)
			{
				*((BYTE *)&wMsgLen + nMsgBytesNum) = lpbyDataAddr[wFront ++];
				nMsgBytesNum ++;
			}
			else
			{
				szMsgBuf[nOffset ++] = lpbyDataAddr[wFront ++];

				// 丢掉
				if (nOffset == 1024)
				{
					ASSERT(0);
				}
				if ((WORD)nOffset == wMsgLen)
				{
					// 回调上层
					m_lpChannelFunc->OnReadChannel(szMsgBuf, nOffset);
					nOffset = 0;
					wMsgLen = 0;
				}
			}

			wFront %= CHANNEL_CACHE_SIZE(m_nChannelBufSize);
		}

	}
}


说明一下:

1:因为是对同一文件进行操作,那么需要互斥,这里用到了文件写锁。可能加锁的时间过长,需要改进。

2.当初线程采取的轮循缓存区,这样感觉不太好,所有使用了有名信号量来通知。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值