Linux进程间通信-消息队列实例(解决mstar平台连续发管道消息数据会丢失的问题)

0、两种消息队列

消息队列就是一个消息的链表,具有特定的格式以及特定的优先级,对消息队列有写入权限的进程可以向其中按照一定的规则添加新消息,对消息队列有读取权限的进程则可以从消息队列中读走消息,这样两个进程间就实现了通信。

消息队列有两种类型,分别为System V以及POSIX,它们的相似之处在于数据的交换单位都是整个消息。本文主要介绍System V 消息队列。

1、消息队列优点

      消息队列与命名管道类似,但少了打开和关闭管道方面的复杂性。使用消息队列并未解决我们在使用命名管道时遇到的一些问题,如管道满时的阻塞问题。消息队列提供了一种在两个不相关进程间传递数据的简单有效的方法。与命名管道相比:消息队列的优势在于,它独立于发送和接收进程而存在,这消除了在同步命名管道的打开和关闭时可能产生的一些困难。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。而且,每个数据块被认为含有一个类型,接收进程可以独立地接收含有不同类型值的数据块。

优点:

      A. 我们可以通过发送消息来几乎完全避免命名管道的同步和阻塞问题。

      B. 我们可以用一些方法来提前查看紧急消息。

缺点:

      A. 与管道一样,每个数据块有一个最大长度的限制。

      B. 系统中所有队列所包含的全部数据块的总长度也有一个上限。

Linux系统中有两个宏定义:

 MSGMAX, 以字节为单位,定义了一条消息的最大长度。

 MSGMNB, 以字节为单位,定义了一个队列的最大长度。

限制:

      由于消息缓冲机制中所使用的缓冲区为共用缓冲区,因此使用消息缓冲机制传送数据时,两通信进程必须满足如下条件。

     (1)在发送进程把写入消息的缓冲区挂入消息队列时,应禁止其他进程对消息队列的访问,否则,将引起消息队列的混乱。同理,当接收进程正从消息队列中取消息时,也应禁止其他进程对该队列的访问。

     (2)当缓冲区中无消息存在时,接收进程不能接收任何消息;而发送进程是否可以发送消息,则只由发送进程是否能够申请缓冲区决定。

2、消息队列实例

2.1 头文件

#ifndef _FIFO_MSG_MANAGER_H_
#define _FIFO_MSG_MANAGER_H_

#include "Hal.h"

#ifdef __cplusplus
extern "C" {
#endif

#define MSG_MGR_MSGQ			1
#define MAX_MSG_BUFFER_SIZE		(256)

#define MSG_MGR_MSGTYPE_FIFO_H	1
#define MSG_MGR_MSGTYPE_FIFO	MSG_PROG_FIFO
#define MSG_MGR_MSGTYPE_ACK		MSG_MGR_MSGTYPE_FIFO+30

#define MSG_MGR_MSGQ_EXIT		MAKE_MSG_ID(MSG_PROG_FIFO, 0, 1)
#define MSG_MGR_FIFO_CMD		MAKE_MSG_ID(MSG_PROG_FIFO, 1, 1)

typedef struct _Msg {
	long	mtype;
	int		msgId;
	int		arg1;
	int 	arg2;
	int 	s32WaitAck;
	int 	iCreateTime100Ms;
	int		paramLen;
	char	paramBuf[MAX_MSG_BUFFER_SIZE];
}Msg;

typedef struct _MsgAck {
	long	mtype;
	int		msgId;
	int 	s32Ret;
}MsgAck;

typedef int (*MSGMGR_HANDLER)(Msg* pMsg);

#if 1  //不能在cardv进程中调用,(cardv调用MsgMgr_Start和MsgMgr_Stop), 可以用在ui或者第三者应用程序中
int MsgMgr_InitMsgQ_SendOnly(); //!!!!不要在cardv中调用
int MsgMgr_DeInitMsgQ_SendOnly(); //!!!!不要在cardv中调用
#endif

#if MSG_MGR_MSGQ
int MsgHandler_Add(long msgType, MSGMGR_HANDLER pfn);
int MsgHandler_Del(long msgType);

int MsgMgr_Start(long MsgType, MSGMGR_HANDLER pfn);
void MsgMgr_Stop();

int MsgMgr_SendMsg(int iMsgId, int arg1, int arg2, char *param, int paramLen, int iWaitAck);
#endif

#ifdef __cplusplus
}
#endif

#endif  //_STATE_MANAGER_H_

2.2 源文件

#include "Hal.h"
#include 
#include 
#include 

#if MSG_MGR_MSGQ
typedef struct _MsgHandlerMap {
	long					mtype;
	MSGMGR_HANDLER		pfn;
}MsgHandlerMap;
#define MSG_MAP_MAX	(5)

static int			s_MsgMgrMsgQueueId = -1;
static pthread_t 	s_MsgMgrMsgQThread;
static pthread_mutex_t s_MsgMgrMsgQrMutex=PTHREAD_MUTEX_INITIALIZER;
static MSGMGR_HANDLER	s_pfnMsgHandler = NULL;
static MsgHandlerMap s_MsgMap[MSG_MAP_MAX] = {0};

static void MsgMgr_Dump()
{
	int i=0;

	for(i=0; imsgId) + sizeof(qbuf->arg1) + sizeof(qbuf->arg2) + sizeof(qbuf->s32WaitAck) + sizeof(qbuf->iCreateTime100Ms) + sizeof(qbuf->paramLen) + qbuf->paramLen;
	size_t msgsz = sizeof(Msg) - sizeof(qbuf->mtype);
	LOGV("type:%d,id=%x\n", qbuf->mtype, qbuf->msgId);
	if (msgsnd(s_MsgMgrMsgQueueId, (void *)qbuf,msgsz, 0) == -1) {
		LOGE("msg[%x]err[%02d][%s]\n", qbuf->msgId, errno, strerror(errno));
		return -1;
	}

	return 0;
}
static int MsgMgr_ReadMessage(Msg *qbuf)
{
	//size_t msgsz = sizeof(qbuf->msgId) + sizeof(qbuf->arg1) + sizeof(qbuf->arg2) + sizeof(qbuf->s32WaitAck) + sizeof(qbuf->iCreateTime100Ms) + sizeof(qbuf->paramLen) + MAX_MSG_BUFFER_SIZE;
	size_t msgsz = sizeof(Msg) - sizeof(qbuf->mtype);
	if (msgrcv(s_MsgMgrMsgQueueId, (void *)qbuf, msgsz, 0-MSG_MGR_MSGTYPE_FIFO, 0) == -1) {
		return -1;
	}

	LOGV("type:%d,id=%x\n", qbuf->mtype, qbuf->msgId);
	return 0;
}
static int MsgMgr_SendAck(MsgAck *qbuf)
{
	LOGV("type:%d,id=%x\n", qbuf->mtype, qbuf->msgId);
	if (msgsnd(s_MsgMgrMsgQueueId, (void *)qbuf, sizeof(qbuf->msgId) + sizeof(qbuf->s32Ret), 0) == -1) {
		LOGE("msg[%x]err[%02d][%s]\n", qbuf->msgId, errno, strerror(errno));
		return -1;
	}
	return 0;
}
static int MsgMgr_WaitAck(MsgAck *qbuf)
{
	if (msgrcv(s_MsgMgrMsgQueueId, (void *)qbuf, sizeof(qbuf->msgId) + sizeof(qbuf->s32Ret), MSG_MGR_MSGTYPE_ACK, 0) == -1) {
		return -1;
	}

	LOGV("type:%d,id=%x\n", qbuf->mtype, qbuf->msgId);
	return 0;
}

static void *MsgMgr_Task(void *argu)
{
    Msg msgBuf;
    int         ret   = 0;
    int         retry = 0;

	LOGD("enter\n");
	
MSG_TASK_RETRY:
    while ((ret = MsgMgr_ReadMessage(&msgBuf)) != -1)
    {
        retry = 0;
		s_pfnMsgHandler = MsgHandler_Get(msgBuf.mtype);
		LOGV("mtype=%x, msgId=%x, s32WaitAck=%d, pfn=%x\n", msgBuf.mtype, msgBuf.msgId, msgBuf.s32WaitAck, s_pfnMsgHandler);
        if(s_pfnMsgHandler) {
			ret = s_pfnMsgHandler(&msgBuf);
        } else {
			ret = -1;
		}
        if (msgBuf.s32WaitAck)
        {
            MsgAck msgAck;
            msgAck.mtype  = MSG_MGR_MSGTYPE_ACK;
            msgAck.msgId  = msgBuf.msgId;
            msgAck.s32Ret = ret;
            MsgMgr_SendAck(&msgAck);
        }

        if (MSG_MGR_MSGQ_EXIT == msgBuf.msgId)
        {
        	LOGD("MSG_MGR_MSGQ_EXIT\n");
        	ret = 0;
            break;
        }
    }

    if (ret == -1)
    {
        LOGE("msg recv err[%02d][%s] retry[%d]\n", errno, strerror(errno), retry);
        if (retry < 5)
        {
            retry++;
            goto MSG_TASK_RETRY;
        }
    }
	LOGD("exit\n");

    pthread_detach(pthread_self());
    pthread_exit(NULL);
    return NULL;
}

static int MsgMgr_InitMsgQ()
{
    key_t msgid;

    msgid = ftok("/tmp/cardv_recv_msg", (('r'<<24)|('m'<<16)|('s'<<8)|('g')));
	if ((s_MsgMgrMsgQueueId = msgget(msgid, IPC_CREAT | 0660)) == -1)
	{
		LOGE("msgget failed errno.%02d is: %s\n", errno, strerror(errno));
		return -1;
	}
	return Comm_CreateThread(&s_MsgMgrMsgQThread, MsgMgr_Task, MID_THREAD_STRACK_SIZE);
}
static int MsgMgr_DeInitMsgQ()
{
	MsgMgr_SendMsg(MSG_MGR_MSGQ_EXIT, 0, 0, NULL, 0, 0);
	pthread_join(s_MsgMgrMsgQThread, NULL);

	/* Remove the queue */
	if (s_MsgMgrMsgQueueId != -1)
	{
		msgctl(s_MsgMgrMsgQueueId, IPC_RMID, 0);
	}
	
	return 0;
}

int MsgMgr_InitMsgQ_SendOnly()  //外部程序调用
{
    key_t msgid;

	if(s_MsgMgrMsgQueueId != -1) return 0;
		
    msgid = ftok("/tmp/cardv_recv_msg", (('r'<<24)|('m'<<16)|('s'<<8)|('g')));
	if ((s_MsgMgrMsgQueueId = msgget(msgid, IPC_CREAT | 0660)) == -1)
	{
		LOGE("msgget failed errno.%02d is: %s\n", errno, strerror(errno));
		return -1;
	}
	return 0;
}
int MsgMgr_DeInitMsgQ_SendOnly()
{
	s_MsgMgrMsgQueueId = -1;
	return 0;
}

int MsgMgr_SendMsg(int iMsgId, int arg1, int arg2, char *param, int paramLen, int iWaitAck)
{
    Msg 	msgBuf;
    MsgAck 	msgAck;
	int ret = 0;

	pthread_mutex_lock(&s_MsgMgrMsgQrMutex);
	do {
	    if (paramLen > MAX_MSG_BUFFER_SIZE)
	    {
	        LOGE("Send cmd failed. cmd=%x, paramLen=%d\n", iMsgId, paramLen);
	        ret = -1;
			break;
	    }

		if(iWaitAck) LOGD("iMsgId=%x, iWaitAck=%d\n", iMsgId, iWaitAck);
	    msgBuf.mtype    = (iMsgId>>24)&0xFF;
	    msgBuf.msgId    = iMsgId;
	    msgBuf.s32WaitAck  = iWaitAck;
		msgBuf.iCreateTime100Ms = Comm_GetBoot100Ms();
	    msgBuf.paramLen = paramLen;
		if(paramLen) {
		    memset(msgBuf.paramBuf, 0, sizeof(msgBuf.paramBuf));
		    memcpy(msgBuf.paramBuf, param, paramLen);
		}
	    if (MsgMgr_SendMessage(&msgBuf) == 0)
	    {
	    	if(iWaitAck) {
		        MsgMgr_WaitAck(&msgAck);
				ret = msgAck.s32Ret;
	    	} else {
				ret = 0;
	    	}
	    }
	}while(0);
	pthread_mutex_unlock(&s_MsgMgrMsgQrMutex);

	if(ret) {
		LOGE("ret = %d\n", ret);
	}
	
    return ret;
}
#endif

//注册回调
int MsgMgr_Start(long MsgType, MSGMGR_HANDLER pfn)
{
	int ret = 0;
	
	do
	{
		//1. 初始化消息队列
		if(MsgMgr_InitMsgQ()) {
			ret = -1;
			break;
		}	

		MsgHandler_DelAll();
		if(pfn) {
			MsgHandler_Add(MsgType, pfn);
		}
	}while(0);

	if(ret) {
		LOGE("ret = %d\n", ret);
	}

	return ret;
}

void MsgMgr_Stop()
{
	MsgMgr_DeInitMsgQ();
	MsgHandler_DelAll();
}

3、使用消息队列解决Sigmstar平台连续发管道消息数据会丢失的问题

处理方式就是改发消息队列,不发管道消息。改写module_system.cpp的cardv_send_to_fifo函数,即对这个函数发fifo的部分屏蔽掉,改成发消息队列。当然原厂也有封装好的消息队列的code,但是为了移植其他平台的方便性,还是重写自己的消息队列部分代码。

UI部分原来发消息的接口是这样的:

Linux进程间通信-消息队列实例(解决mstar平台连续发管道消息数据会丢失的问题)_第1张图片

改成消息队列的形式:

Linux进程间通信-消息队列实例(解决mstar平台连续发管道消息数据会丢失的问题)_第2张图片

4、使用方法

在cardv中修改fifo的接口:

Linux进程间通信-消息队列实例(解决mstar平台连续发管道消息数据会丢失的问题)_第3张图片

你可能感兴趣的:(linux,服务器,网络)