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 消息队列可以根据类型来选择消息,更加灵活。