码迷,mamicode.com
首页 > 其他好文 > 详细

进程间通信(二)——Posix消息队列

时间:2014-08-01 22:46:32      阅读:315      评论:0      收藏:0      [点我收藏+]

标签:des   style   color   使用   os   io   文件   数据   

1.概述

消息队列可认为是消息链表。有足够写权限的线程可以往队列中放置消息,有足够读权限的进程可以从队列中取走消息。每个消息是一个记录,由发送着赋予一个优先级。

在像队列中写入消息时,不需要某个进程在该队列上等待消息到达。这与管道不同,管道必须现有读再有写。

消息队列具有随内核的持续性,与管道不同。进程结束后,消息队列中消息不会消失。当管道最后一次关闭,其中的数据将丢弃。

消息队列具有名字,可用于非亲缘关系的进程间。

Posix消息队列读总是返回最高优先级的最早消息,而System V消息队列的读可以返回任意优先级的。

往空消息队列中放置消息,Posix消息队列会产生一个信号或启动一个线程。而System V不提供这个功能。

 

2.消息队列相关函数

2.1. 创建,关闭,删除

#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag, …

               /* mode_t mode, struct mq_qttr *attr */);

返回:成功:消息队列描述符 出错:-1

用于创建一个新的或者打开一个已存在的消息队列。

oflag是O_RDONLY, O_WRONLY或O_RDWR之一,可能按位或上O_CREAT, O_EXCL或O_NONBLOCK。

创建新队列时,mode和attr是必须的。attr=NULL使用默认属性

mq_open返回的消息队列描述符,它不必是像普通描述符那么样的短整形(很可能不是)。

 

int mq_close(mqd_t mqdes);

关闭已打开的消息队列。调用进程不能在使用这个描述符了,但是消息队列没有从系统中删除。

进程终止时,所有打开者的消息队列都关闭,就像调用mq_close();

 

从系统中删除name

int mq_unlink(const char *name);

成功:0;出错:-1;

消息队列保存着当前打开着的文件的引用计数,mq_unlink会删除name,但是消息队列的删除要到最后一个mq_close()发生为止。

 

消息队列至少具有随内核的持续性。当没有进程打开某个消息队列时,该队列的消息仍然存在,知道mq_unlink并让应用计数为0才删除该队列。

 

2.2. 消息队列属性

#include <mqueue.h>

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);

成功:0;失败:-1;

struct mq_attr {

long mq_flags;             //message queue flag: 0, O_NONBLOCK

long mq_maxmsg;        //max number of message allowed on queue

long mq_msgsize;        //max size of a message (in bytes)

long mq_curmsgs;       //number of message currlently on queue
}

创建新队列时,可以指定mq_maxmsg和mq_msgsize属性,但是mq_open会忽略另外两个参数。

mq_setattr设置队列属性时,只使用mq_flags用于设置或着清除非阻塞标志,另两个忽略。

消息大小和数目只能在创建队列时指定。

 

2.3. 接受和发送

#include <mqueue.h>

int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);

成功:0;出错: -1;

ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio);

成功: 消息中字节数; 出错: -1;

用于向队列中放置消息和从队列中取出消息。

mq_receive中len不能小于mq_msgsize,否则会返回EMSGSIZE。

prio为消息优先级,必须小于MQ_PRIO_MAX。

若不需要使用优先级不同的消息,指定mq_send为0优先级,mq_receive优先级为NULL。

 

3. 异步事件通知

Posix消息队列允许异步消息通知,即当有一个消息放到某个空消息队列中,会产生某种通知:

(1) 产生一个信号

(2) 创建一个线程执行指定函数

#include <mqueue.h>

int mq_notify(mqd_t mqdes, const struct sigevent *notification);

成功: 0; 出错:-1;

#include <signal.h>

union sigval {

int sival_int;

int *sival_ptr;
}

struct sigevent {

int sigev_notify;          //通知方式:SIGEV_{NONE, SIGNAL, THREAD}

int sigev_signo;          //signal number if SIGEV_SIGNAL

union sigval sigev_value;//传递给信号或者线程的参数

void (*sigev_notify_function)(union sigval);

pthread_attr_t *sigev_notify_attrbutes;
}

在接受到通知之后,会被复位成默认行为,因此必须再注册。

在队列变空前不会再接受到通知,要保证在读出消息之前重新注册。否则在消息变为空后再注册,这段时间内可能又有消息放到队列中造成不会再通知。

 

4.通知的处理方式

4.1. 信号通知

不可以简单的在信号处理程序中调用mq_receive, 因为它不是异步信号安全函数(可以从信号处理程序中调用的函数)。一般只有sem_pos,read,write是信号安全的。

也不可以简单使用mq_receive接受消息,因为这只能接受到一个消息,其他的消息可能永远不会被取出,应该总是使用非阻塞方式读数据。

 

程序处理方式:

方法1:使用sigsuspend()等待信号的发生,通过在信号处理程序中设置全局变量,被信号唤醒后再处理数据。

方法2:由于消息队列描述符不是普通描述符,不可以直接使用select。通过管道和在信号处理程序中调用write唤醒select的方式。

方法3:通过sigwait()函数等待某个信号发生,然后再处理,类似于select。这是最好的方式。

#include <signal.h>

int sigwait(const sigset_t *set, int *sig);

成功:0; 出错:正的EXXX值。

函数阻塞到set中某个信号发生,sig返回信号值。

这叫做“同步地等待一个异步信号”,使用信号却没有涉及异步信号处理。

此函数往往在多线程话程序中使用,有EXXX错误。在多线程话程序中不能使用sigprocmask,要使用pthread_sigmask。

代码示例:

 

4.2线程处理方式,这是最好的

注意sigev的设置。

 

示例:

 

进程间通信(二)——Posix消息队列,布布扣,bubuko.com

进程间通信(二)——Posix消息队列

标签:des   style   color   使用   os   io   文件   数据   

原文地址:http://www.cnblogs.com/hancm/p/3885824.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!