POSIX消息队列的异步通信机制

POSIX消息队列的异步通信机制

 

http://blog.csdn.net/bat603

 

         对于消息队列的读取操作,不管是采用System V方式,还是采用POSIX方式,一般的做法都是通过定时轮询(polling),这就消耗了一定的CPU时间。在这里我们介绍一下POSIX消息的异步事件通知(asynchronous eventnotification),当消息队列中有消息时会触发通知机制。其中通知机制有两个方式:产生一个信号或创建一个线程去执行指定的函数。我们采用第二种方法。

 

1.      主要函数介绍

考虑到篇幅问题,我们只介绍设置通知方式的函数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.  当消息机制触发后,需要重新注册,在我们的应用中,当线程函数触发后需要重新注册线程函数。在后面的代码中可以看到。

 

2.      简单示例

说了这么多,还不如来个例子,这样理解更快。

 

#include<pthread.h>

#include<mqueue.h>

#include<assert.h>

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

 

//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, &not) == -1) die("mq_notify");

         pause();    /* Process will be terminated by threadfunction */

}

 

3.      对POSIX消息队列线程通知机制的封装

为了更加简单的使用POSIX消息队列进行进程间通信,我对它进行了封装了,并使用了

线程通知机制。

头文件cppmq.h

/***********************************************************************

 ** Copyright (c)2009,

 ** All rights reserved.

 **

 ** File name  : cppmq.h

 ** Author     : lizp (lizp.net@gmail.com)

 ** Date       : 2009-12-9 10:58:43

 ** Comments   : 将消息队列进行封装,异步接收,方便使用

http://blog.csdn.net/bat603

 ***********************************************************************/

 

#pragma once

 

#define MAX_PATH 512

#include <mqueue.h>

 

#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, ...);

 

源代码cppmq.cpp

 

#include <stdlib.h>

#include <stdio.h>

#include <mqueue.h>

#include <errno.h>

#include <signal.h>

#include <pthread.h>

#include <sys/syscall.h>

#include <stdarg.h>

#include <ctype.h>

#include <unistd.h>

#include <string.h>

 

#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(lizp.net@gmail.com)

** Date       : 2010-01-0113:06:54

** Comments   : 接收的使用方法

http://blog.csdn.net/bat603

***********************************************************************/

#include <stdlib.h>

#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <sys/resource.h>

 

#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(lizp.net@gmail.com)

** Date       : 2010-01-0113:06:40

** Comments   : 发送的使用方法

http://blog.csdn.net/bat603
***********************************************************************/

#include <stdlib.h>

#include <stdio.h>

#include <unistd.h>

#include <string.h>

 

#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();

}

 

Makefile

         #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)

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux系统提供了各种系统调用API用于进程之间的通信:    无名管道PIPE    命名管道FIFO    消息队列    共享内存    信号量    文件锁    信号signal....其中还包括system V和POSIX 两种接口标准,除此之外,Linux系统自身还扩展了自己的一套API接口用于进程间通信,比如signalfd、timerfd、eventfd等。本视频教程为《Linux系统编程》第05期,本期课程将会带领大家学习Linux下将近15种进程间通信IPC工具的使用,了解它们的通信机制、编程实例、使用场景、内核中的实现以及各自的优缺点。本课程会提供PDF版本的PPT课件和代码,学员购买课程后可到课程主页自行下载嵌入式自学路线指导图:------------------------------------------------------------------------------------------------------                   《嵌入式工程师自我修养》嵌入式自学系列教程                                          作者:王利涛------------------------------------------------------------------------------------------------------一线嵌入式工程师精心打造,嵌入式学习路线六步走: 第 1 步:Linux三剑客零基础玩转Linux+UbuntuGit零基础实战:Linux开发技能标配vim从入门到精通基础篇:零基础学习vim基本命令vim从入门到精通定制篇:使用插件打造嵌入式开发IDEmakefile工程实践基础篇:从零开始一步一步写项目的Makefilemakefile工程实践第2季:使用Autotools自动生成Makefile软件调试基础理论printf打印技巧Linux内核日志与打印使用QEMU搭建u-boot+Linux+NFS嵌入式开发环境第 2 步:C语言嵌入式Linux高级编程第1期:C语言进阶学习路线指南第2期:计算机架构与ARM汇编程序设计第3期:程序的编译、链接和运行原理第4期:堆栈内存管理第6期:数据存储与指针第7期:嵌入式数据结构与Linux内核的OOP思想第8期:C语言的模块化编程第9期:CPU和操作系统入门      搞内核驱动开发、光会C语言是不行的!      你还需要学习的有很多,包括:计算机体系架构、ARM汇编、程序的编译链接运行原理、CPU和操作系统原理、堆栈内存管理、指针、linux内核中的面向对象思想、嵌入式系统架构、C语言的模块化编程.....第 3 步:Linux系统编程第00期:Linux系统编程入门第01期:揭开文件系统的神秘面纱第02期:文件I/O编程实战第03期:I/O缓存与内存映射第04期:打通进程与终端的任督二脉第05期:进程间通信-------------------we are here!‍    第 4 步:Linux内核编程‍    练乾坤大挪移,会不会九阳神功,是一道坎。搞驱动内核开发,懂不懂内核也是一道坎。第 5 步:嵌入式驱动开发    芯片原理、datasheet、硬件电路、调试手段、总线协议、内核机制、框架流程....第 6 步:项目实战    嵌入式、嵌入式人工智能、物联网、智能家居...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值