7.2 POSIX 消息队列

POSIX 消息队列允许进程之间以消息的形式交换数据。

打开、关闭和断开消息队列

创建或打开一个消息队列:

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>

/*
@param name 	消息队列标识,一般为以 / 开头的字符串
@param oflag	可取:
            	- O_CREAT 	 不存在时创建
                - O_EXCL	 与 O_CREAT 搭配使用,排它地创建队列
                - O_RDONLY	 只读打开
                - O_WRONLY	 只写打开
                - O_RDWR	 读写打开
                - O_NONCLOCK 非阻塞模式打开
@param mode	与 attr 在指定 O_CREAT 创建消息队列时使用
            	mode 为位掩码指定了新消息队列上的权限,与文件权限一致
@param attr	指定消息队列的特性,一般指定为 NULL 采用默认特性

@return	返回消息队列描述符,失败返回 -1

*/
mqd_t mq_open(const char *name, int oflag, ...
              /* mode_t mode, struct mq_attr *attr */);

关闭一个消息队列:

#include <mqueue.h>
// 成功返回 0,失败返回 -1
int mq_close(mqd_t mqdes);

此调用将关闭消息队列描述符,进程在不使用消息队列时应该关闭,放置出现进程耗尽消息队列描述符的情况。mq_close 并不会删除该队列。 删除一个消息队列:

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

mq_unlink 函数删除通过 name 标识的消息队列,并将队列标记为在所有进程使用完该队列之后销毁该队列(这可能意味着会立即删除,前提是所有打开该队列的进程已经关闭了该队列)。

消息队列特性

mq_open 中的 attr 可以指定新创建的消息队列的特性,它是一个 mq_attr 结构变量,定义如下:

#inclued <mqueue.h>

struct mq_attr {
    long mq_flags;	// 0 或者 O_NONBLOCK
    long mq_maxmsg;	// 消息队列的最大消息数量
    long mq_msgsize;	// 消息队列每条消息的最大字节数量
    long mq_curmsgs; 	// 消息队列当前的消息数量
};

其中 mq_maxmsg 和 mq_msgsize 在消息队列创建时就确定下来,之后便无法改变。系统也会对这两个特性有一个限制。 获取消息队列特性:

#include <mqueue.h>
// 返回消息队列特性,可以通过此调用获取当前消息队列的消息数量
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

修改消息队列特性:

#include <mqueue.h>
// 修改消息队列特性,若 oldattr 不为 NULL,则返回原来的消息特性
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);

SUSv3 规定使用 mq_setattr 唯一可以修改的特性就是 O_NONBLOCK 的状态。

交换消息

发送一个消息:

#include <mqueue.h>

/*
@brief 向指定的消息队列发送消息
@param mqdes 	消息队列描述符
@param msg_ptr 	需要发送的消息指针
@param msg_len 	消息长度,必须小于队列的 mq_msgsize
@param msg_prio 消息优先级,0 为最低,消息队列中的消息按优先级排序
                优先级有一个系统上限,可通过 sysconf(_SC_MQ_PRIO_MAX) 获取
@return 成功返回 0 失败返回 -1
*/
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);

如果消息队列已经满了,那么后续的 mq_send 调用会阻塞直到队列存在可用空间。或者开启了 O_NONBLOCK 标记,会立即返失败并返回 EAGIN 错误。 接收一个消息:

#include <mqueue.h>

/*
@brief 从 mqdes 引用的消息队列中删除一条优先级最高、存在时间最长的消息,
       并将此消息放置在 msg_ptr 指定的缓冲区中
@param msg_len 	缓冲区大小,必须大于等于队列的 mq_msgsize 特性
               	否则调用失败并返回 EMSGSIZE 错误
@param msg_prio 如果不为 NULL,则接收优先级为 msg_prio 指定的消息
*/
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);

如果消息队列当前为空,那么 mq_receive 会阻塞直到存在可用的消息或在 O_NONBLOCK 标记起作用时会立即失败并返回 EAGAIN 错误。

与 System V 消息队列比较

POSIX 消息队列具备的优势:

  • 具有消息通知特性,允许消息进入一个空队列时通过信号或者线程函数异步通知(消息通知这里没有做介绍)。

  • Linux 上可以使用 poll、select、epoll 来监控 POSIX 消息队列,System V 则没有。

有以下劣势:

  • 可移植性较差,Linux 内核 2.6.6 之后提供了对消息队列的支持。

  • System V 消息队列可以根据类型来选择消息,更加灵活。

最后更新于