POSIX消息队列的异步通信机制
http://blog.csdn.net/bat603
对于消息队列的读取操作,不管是采用System V方式,还是采用POSIX方式,一般的做法都是通过定时轮询(polling),这就消耗了一定的CPU时间。在这里我们介绍一下POSIX消息的异步事件通知(asynchronous eventnotification),当消息队列中有消息时会触发通知机制。其中通知机制有两个方式:产生一个信号或创建一个线程去执行指定的函数。我们采用第二种方法。
考虑到篇幅问题,我们只介绍设置通知方式的函数mq_notify,POSIX消息队列的其他函
数,请查询相关资料。
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
返回:成功时为0,出错时为-1
其中mqd_t是消息队列描述字。
http://blog.csdn.net/bat603
struct sigevent的结构是:
union sigval { /*传递的参数*/
int sival_int; /* 信号机制传递的参数 */
void *sival_ptr; /* 若是线程机制传递的参数 */
};
struct sigevent {
int sigev_notify; /* 设置通知机制方法,线程为SIGEV_THREAD,信号为SIGEV_SIGNAL*/
int sigev_signo; /* 若是信号机制,该参数设置为触发的信号 */
union sigval sigev_value;/* 传递的参数*/
void (*sigev_notify_function)(union sigval);
/* 若是线程机制,该参数为线程函数*/
void *sigev_notify_attributes;
/* 线程函数的属性 */
};
当notification为非空时表示向该消息队列注册一个线程函数(当前只介绍线程通知机
制),为空时表示将该消息队列注册的线程函数注销。
需要注意的几点:
1. 只需注册一个线程函数。
2. 消息机制触发条件是:在消息队列为空的情况下有数据到来才会触发,当消息队列不为空时,有数据到来不触发。
3. 当消息机制触发后,需要重新注册,在我们的应用中,当线程函数触发后需要重新注册线程函数。在后面的代码中可以看到。
说了这么多,还不如来个例子,这样理解更快。
#include
#include
#include
#include
#include
#include
//http://blog.csdn.net/bat603
#definedie(msg) { perror(msg); exit(EXIT_FAILURE); }
/*线程函数*/
staticvoid tfunc(union sigval sv)
{
struct mq_attr attr;
ssize_t nr;
void *buf;
mqd_t mqdes = *((mqd_t *) sv.sival_ptr);/*已打开的消息队列的描述字*/
/*Determine max. msg size; allocate buffer to receive msg */
if(mq_getattr(mqdes, &attr) == -1) die("mq_getattr");
buf =malloc(attr.mq_msgsize);
if(buf == NULL) die("malloc");
nr= mq_receive(mqdes, buf, attr.mq_msgsize, NULL);
if(nr == -1) die("mq_receive");
printf("Read%ld bytes from MQ0, (long) nr);
free(buf);
exit(EXIT_SUCCESS); /* Terminate the process */
}
int main(intargc, char *argv[])
{
mqd_tmqdes;
structsigevent not;
assert(argc== 2);
/*打开一个消息队列*/
mqdes= mq_open(argv[1], O_RDONLY);
if(mqdes == (mqd_t) -1) die("mq_open");
not.sigev_notify= SIGEV_THREAD;/*线程触发机制*/
not.sigev_notify_function= tfunc;
not.sigev_notify_attributes= NULL;
not.sigev_value.sival_ptr= &mqdes; /*传递线程函数的参数,是消息队列的描述字 */
if(mq_notify(mqdes, ¬) == -1) die("mq_notify");
pause(); /* Process will be terminated by threadfunction */
}
为了更加简单的使用POSIX消息队列进行进程间通信,我对它进行了封装了,并使用了
线程通知机制。
/***********************************************************************
** Copyright (c)2009,
** All rights reserved.
**
** File name : cppmq.h
** Author : lizp ([email protected])
** Date : 2009-12-9 10:58:43
** Comments : 将消息队列进行封装,异步接收,方便使用
http://blog.csdn.net/bat603
***********************************************************************/
#pragma once
#define MAX_PATH 512
#include
#ifdef _DEBUG
#define DEBUG_PRT(fmt, ...) debug_printf(__FILE__,__LINE__, fmt, ## __VA_ARGS__)
#define INFO_PRT(fmt, ...) info_printf(fmt,## __VA_ARGS__)
#else
#define DEBUG_PRT(fmt, ...) {; }
#define INFO_PRT(fmt, ...) {; }
#endif
typedef struct _tagMqAttr
{
long mq_flags; /* Flags: 0 or O_NONBLOCK */
long mq_maxmsg; /* Max. # of messages on queue */
long mq_msgsize; /* Max. message size (bytes) */
long mq_curmsgs; /* # of messages currently in queue */
}_MQ_ATTR;
class CBaseMQ
{
public:
CBaseMQ(){}
CBaseMQ(const char*mq_name);
virtual ~CBaseMQ();
//得到消息队列的属性信息
intget_attr(_MQ_ATTR& attr);
int set_attr(_MQ_ATTRattr);
//mq_size 队列中最大消息数, msg_size 每个消息最大大小
virtual int open(intmq_size, int msg_size){return -1;};
virtual intclose(){return -1;};
private:
protected:
long _mq_id;
char_mq_name[MAX_PATH];
};
class CMQSender : public CBaseMQ
{
public:
CMQSender(const char*mq_name);
virtual ~CMQSender();
virtual int open(intmq_size = 64, int msg_size = 256);
virtual int close();
int send(void* data,int len, int prio = 0);
private:
CMQSender(){}
private:
};
class CMQRecver : public CBaseMQ
{
public:
typedef void(*CALLBACK_FUN_RECV)(void* data, int len);
public:
CMQRecver(const char*mq_name);
virtual ~CMQRecver();
virtual int open(intmq_size = 64, int msg_size = 256);
virtual int close();
virtual voidon_recv(void* data, int len){};
voidset_callback_fun(CALLBACK_FUN_RECV fun_recv);
private:
CMQRecver(){}
//static void*TheThread(void *param);
static voidTheThread(union sigval sigev_value);
virtual int Thread();
private:
bool _exit;
struct mq_attr _attr;
struct sigevent _sigev;
CALLBACK_FUN_RECV_fun_recv;
};
void debug_printf(const char *file, int line, const char *fmt, ...);
void info_printf(const char *fmt, ...);
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "cppmq.h"
void debug_printf(const char *file, int line, const char *fmt, ...)
{
va_list ap;
#ifdef WIN32
DWORD pid =GetCurrentThreadId();
#else
//pthread_t pid =pthread_self();
pid_t pid = (long)syscall(__NR_gettid);
#endif
fprintf(stdout,"(%s:%d:PID %d:TID %d)\n", file, line, getpid(), pid);
va_start(ap, fmt);
vfprintf(stdout, fmt,ap);
va_end(ap);
fprintf(stdout,"\n");
fflush(stdout);
}
void info_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stdout, fmt,ap);
va_end(ap);
fprintf(stdout,"\n");
fflush(stdout);
}
CBaseMQ::CBaseMQ(const char* mq_name)
{
if ( mq_name != NULL )
strcpy(_mq_name,mq_name);
_mq_id = -1;
}
CBaseMQ::~CBaseMQ()
{
//mq_unlink(_mq_name);
}
int CBaseMQ::get_attr(_MQ_ATTR& attr)
{
returnmq_getattr((mqd_t)_mq_id, (struct mq_attr*)&attr);
}
int CBaseMQ::set_attr(_MQ_ATTR attr)
{
struct mq_attrold_attr;
returnmq_setattr((mqd_t)_mq_id, (struct mq_attr*)&attr, &old_attr);
}
CMQSender::CMQSender(const char* mq_name):CBaseMQ(mq_name)
{
}
CMQSender::~CMQSender()
{
}
int CMQSender::open(int mq_size /*= 128*/, int msg_size /*= 128*/)
{
struct mq_attrnew_attr;
new_attr.mq_maxmsg =mq_size;
new_attr.mq_msgsize =msg_size;
_mq_id =(long)mq_open(_mq_name, O_WRONLY | O_NONBLOCK | O_CREAT, 0600, &new_attr);
if ( _mq_id == -1 )
{
DEBUG_PRT("mq_open(%s,O_WRONLY | O_CREAT, 0600, NULL) error:%s", _mq_name, strerror(errno));
return -1;
}
mq_getattr((mqd_t)_mq_id,&new_attr);
DEBUG_PRT("mq_maxsize:%d,mq_maxmsg:%d", new_attr.mq_msgsize, new_attr.mq_maxmsg);
return 0;
}
int CMQSender::close()
{
returnmq_close((mqd_t)_mq_id);
}
int CMQSender::send(void* data, int len, int prio)
{
int ret =mq_send((mqd_t)_mq_id, (char*)data, len, prio);
if ( ret == -1 )
{
DEBUG_PRT("mq_senderror:%s", strerror(errno));
}
return ret;
}
CMQRecver::CMQRecver(const char* mq_name):CBaseMQ(mq_name)
{
_exit = true;
_fun_recv = NULL;
}
CMQRecver::~CMQRecver()
{
}
int CMQRecver::open(int mq_size /*= 128*/, int msg_size /*= 128*/)
{
struct mq_attrnew_attr;
memset(&new_attr,0, sizeof(struct mq_attr));
new_attr.mq_maxmsg =mq_size;
new_attr.mq_msgsize =msg_size;
_mq_id =(long)mq_open(_mq_name, O_RDONLY | O_NONBLOCK | O_CREAT, 0600,&new_attr);// | O_CREAT, 0600, NULL);
if ( _mq_id == -1 )
{
DEBUG_PRT("mq_open(%s,O_RDONLY | O_NONBLOCK | O_CREAT, 0600, &new_attr) error:%s", _mq_name,strerror(errno));
return -1;
}
mq_getattr((mqd_t)_mq_id,&_attr);
DEBUG_PRT("mq_maxsize:%d,mq_maxmsg:%d", _attr.mq_msgsize, _attr.mq_maxmsg);
_sigev.sigev_notify =SIGEV_THREAD;
_sigev.sigev_value.sival_ptr= this;
_sigev.sigev_notify_function= TheThread;
_sigev.sigev_notify_attributes= NULL;
mq_notify((mqd_t)_mq_id,&_sigev);
ssize_t n;
void *buff;
buff =malloc(_attr.mq_msgsize);
while ( (n =mq_receive((mqd_t)_mq_id, (char*)buff, _attr.mq_msgsize, NULL)) >= 0) {
DEBUG_PRT("read%ld bytes", (long) n);
on_recv(buff,n);
}
free(buff);
return 0;
}
int CMQRecver::close()
{
_exit = true;
returnmq_close((mqd_t)_mq_id);
}
void CMQRecver::set_callback_fun(CALLBACK_FUN_RECV fun_recv)
{
_fun_recv = fun_recv;
}
void CMQRecver::TheThread(union sigval sigev_value)
{
// pthread_detach(pthread_self());
// pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
// pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
INFO_PRT("Willenter TheThread");
CMQRecver *jthread;
jthread = (CMQRecver*)sigev_value.sival_ptr;
int ret =jthread->Thread();
DEBUG_PRT("Willleave TheThread, return:%d", ret);
return;
}
int CMQRecver::Thread()
{
ssize_t n;
void *buff;
printf("notify_threadstarted\n");
buff =malloc(_attr.mq_msgsize+1);
mq_notify((mqd_t)_mq_id,&_sigev); /*reregister */
while ( (n =mq_receive((mqd_t)_mq_id, (char*)buff, _attr.mq_msgsize, NULL)) >= 0) {
DEBUG_PRT("read%ld bytes", (long) n);
((char*)buff)[n]= 0;
on_recv(buff,n);
if (_fun_recv )
_fun_recv(buff,n);
}
if (errno != EAGAIN)
DEBUG_PRT("mq_receiveerror:%s", strerror(errno));
free(buff);
return 0;
}
接收进程
/***********************************************************************
** Copyright (c)2007, lizp.net
** All rights reserved.
**
** File name : receiver.cpp
** Author : lizp([email protected])
** Date : 2010-01-0113:06:54
** Comments : 接收的使用方法
http://blog.csdn.net/bat603
***********************************************************************/
#include
#include
#include
#include
#include
#include "cppmq.h"
//#include "comlog.h"
class receiver : public CMQRecver
{
public:
receiver (const char*name):CMQRecver(name)
{
}
virtual voidon_recv(void* data, int len)
{
DEBUG_PRT("on_recv:%s,%d", (char*)data, len);
};
};
int main()
{
receiver r("/mq");
r.open(10, 8192);
while(1)
{
sleep(1);
}
}
发送进程
/***********************************************************************
** Copyright (c)2007, lizp.net
** All rights reserved.
**
** File name : sender.cpp
** Author : lizp([email protected])
** Date : 2010-01-0113:06:40
** Comments : 发送的使用方法
http://blog.csdn.net/bat603
***********************************************************************/
#include
#include
#include
#include
#include "cppmq.h"
int main()
{
CMQSendersender("/mq");
sender.open(100,8192);
char* ptr ="/usr/local/was/data/20091210/123456/2.jpg";
int ptr_len =strlen(ptr);
printf("ptr is=%s", ptr);
int i = 0;
int ret = -1;
// while (1)
{
ret =sender.send(ptr, ptr_len);
DEBUG_PRT("ret:%d,send times:%d", ret, i++);
sleep(3);
}
sender.close();
}
#http://blog.csdn.net/bat603
CC = g++
CFLAGS = -g -lrt -D_DEBUG
MYLIB = ./libcppmq.a
PROGS = receiver sender
all: ${PROGS}
.cpp.o:
$(CC) $(CFLAGS) -c$<
receiver: receiver.o $(MYLIB)
$(CC) -o $@ receiver.o$(MYLIB) $(CFLAGS)
sender: sender.o $(MYLIB)
$(CC) -o $@ sender.o$(MYLIB) $(CFLAGS)
lib:cppmq.o
ar cr $(MYLIB) $?
#$(RANLIB) $@
clean :
rm -f a.out core *.o*.t
rm -f $(MYLIB)$(PROGS)