您的位置:首页 > 其它

Posix消息队列

2016-03-11 19:53 447 查看
消息队列是一种IPC机制,有Posix消息队列和System V消息队列两种类型,它们有许多相似之处,但也有一些差别:对Posix消息队列的读总是返回优先级最高的最早的消息,而对System V消息队列的读则可以返回任意指定优先级的消息;当往空队列放置消息时,Posix消息队列允许产生一个信号或启动一个线程,而System V消息队列则不能提供类似的机制。消息队列可认为是一个消息链表,队列中的每个消息都有几个共同的属性:一个无符号整数优先级(Posix)或一个长整数类型(System V)、消息的数据部分长度(可以为0)、数据本身(如果长度大于0)。Posix消息队列的使用有一些注意事项,可在shell终端通过”man 7 mq_overview“查看。

1、消息队列的创建与关闭

创建消息队列使用mq_open函数:

#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);


mq_open成功时返回一个消息队列描述符,类型为mqd_t,失败时返回-1并设置相应的errno。参数name、oflag、mode的用法和常见的open函数的几个参数的用法相同,attr为消息队列的属性,当创建一个新的消息队列时要指定mode和attr参数,attr可以是NULL,这时消息队列的属性为默认值。参数name的格式比较特殊,它是个C风格的字符串且以斜线”/”开始但后面的字符不能包括斜线,长度不能超过NAME_MAX即255。mq_open有一个限制MQ_OPEN_MAX,一个进程同时拥有的打开的消息队列描述符不能超过这个值。

关闭打开的消息队列使用mq_close函数:

#include <mqueue.h>
int mq_close(mqd_t mqdes);


mq_close成功时返回0,失败时返回-1并设置相应的errno,功能同普通的close函数关闭一个不用的文件描述符一样,但它并不从系统中删除,从系统中删除要使用mq_unlink函数。

#include <mqueue.h>
int mq_unlink(const char *name);


mq_unlink成功时返回0,失败时返回-1并设置相应的errno。

消息队列在编译链接时用到了librt库,使用-lrt选项。

消息队列内部维护着一个打开的消息队列描述符的引用计数,mq_close和mq_unlink执行成功后,都会使引用计数减1,当引用计数为0时,消息队列才会被真正的清除,但对于mq_unlink则不同,即使引用计数不为0,消息队列在系统中的name也会被删除,只是消息队列并没有被真正的清除,直到最后一个mq_close将引用计数减为0。由于Posix消息队列具有随内核的持续性,如果不调用mq_unlink,消息队列在系统关闭时才会消失。

消息队列的位置——

在Linux上初次使用mq_open创建消息队列时,在本地文件系统很可能找不到消息队列的文件位置,原因是消息队列在虚拟系统中创建,以root权限使用如下mount命令后就可以在”/dev/mqueue”下找到了。

$ mkdir /dev/mqueue
$ mount -t mquque none /dev/mqueue


2、消息队列的属性

获取与设置消息队列的属性,有如下两个函数:

#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);


mq_getatt与mq_setattr成功时返回0,失败时返回-1并设置相应的errno。消息队列结构mq_attr有以下几个属性:

struct mq_attr {
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 */
};


关于消息队列结构成员的几个值可在下列目录查看(它们的值分别为10、10、8192、8192、256):

/proc/sys/fs/mqueue/msg_default
/proc/sys/fs/mqueue/msg_max
/proc/sys/fs/mqueue/msgsize_default
/proc/sys/fs/mqueue/msgsize_max
/proc/sys/fs/mqueue/queues_max


mq_attr结构指针可作为mq_open的参数,在创建新的消息队列时指定mq_maxmsg和mq_msgsize,但其它两个属性将被mq_open忽略。

mq_getatt用于获取消息队列的属性。

mq_getatt用于设置消息队列的属性,但是只能设置mq_flags,其它三个属性将被忽略,mq_maxmsg和mq_msgsize由mq_open创建时设置,mq_curmsgs只能获取而不能设置。

3、消息的发送与获取

消息的发送与获取使用下面两个函数:

#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);


mq_send成功时返回0,失败时返回-1并设置errno,它往消息队列中放置一个消息,每个消息有一个优先级,它是一个小于MQ_PRIO_MAX的无符号整数。

mq_receive成功时返回消息长度,失败时返回-1并设置errno,它从消息队列中取走一个消息,但不能设置优先级(这点不同于System V消息队列的msgrcv),总是返回指定消息队列的最高优先级的最早消息,这个优先级可随消息的内容和长度一并返回。mq_receive函数的msg_len参数需要保证能容纳消息的最大长度,即消息队列属性的msgsize,否则发生错误EMSGSIZE。

4、消息通知

在使用消息队列时会遇到这么一种情况,从空队列获取消息时,如果设置为阻塞,那么将一直阻塞在mq_receive上,期间不能做其它事情,如果为非阻塞, mq_receive将马上出错返回,为了能够获取后续发送来的消息,要循环去获取消息或者叫做轮询poll,这又会浪费CPU时间,也是不值当的,一个可行的方法是使用消息队列的异步事件通知mq_notify,它可以告诉我们何时有一个消息放到了空的消息队列。

#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);


mq_notify成功时返回0,失败时返回-1并设置errno。mq_notify通知有两种形式,产生一个信号,或者创建一个线程并执行指定的函数,使用时有如下一般规则。

如果sevp参数非空,那么当前进程希望在有个消息到达所指定队列而且该队列先前为空时得到通知,该进程被注册为接收该队列的通知。

如果sevp参数为空指针,而且当前进程被注册为接收所指定队列的通知,那么现有注册将被撤销。

任意时刻只有一个进程可以被注册为接收某个给定队列的通知。

当有一个消息到达某个先前为空的队列,而且已有一个进程被注册为接收该消息队列的通知时,只有任何线程没有阻塞在该队列mq_receive调用的前提下,通知才会发出,在mq_recieve调用中的阻塞比任何注册的通知都优先。

当该通知被发送给注册它的进程时,其注册即被撤销,如果需要的话,该进程必须再次调用mq_notify以重新注册。

// 例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: