关于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.当初线程采取的轮循缓存区,这样感觉不太好,所有使用了有名信号量来通知。