消息队列是消息的链接表,存储在内核中,由消息队列标识符标识。有足够写权限的进程可往队列中放置消息,有足够读权限的进程可从队列中取走消息。POSIX 和 system V 下的消息队列略有不同,主要体现在一下几个方面:
- 一般来说 POSIX 的接口要比 system V 的简单,但是 system V 的可已移植性更好,几乎所有的 UNIX 系统都支持
- 对 POSIX 消息队列的读总是返回最高优先级的最早消息,对 system V 消息队列的读则可以返回任意指定优先级的消息
- 当往一个空队列放置一个消息时,POSIX 消息队列允许产生一个信号或者启动一个线程,system V 消息队列则不提供类似的机制
这里我们以 POSIX 下的消息队列为例来进行讲解。
消息队列的创建
消息队列的 mq_open 函数如同操作文件的 open 函数,用于打开或创建一个消息队列,其接口定义如下:
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#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);
消息队列中的 mode 位同普通文件操作的 mode 位使用是完全一致的。
调用 fork 之后,子进程也获得了消息队列的描述符,因此子进程同样也可以打开这个消息队列。当子进程调用 exec 之后,由于内核实现中消息队列的描述符带有 O_CLOEXEC 标志位,所以其打开的消息队列会被自动关闭。
当进程退出时,所有打开的消息队列都会被关闭。
消息队列的关闭和销毁
mq_close 函数用于消息队列的关闭,这个函数和文件关闭的 close 函数十分相似,其接口定义如下:
#include <mqueue.h>
int mq_close(mqd_t mqdes);
POSIX 消息队列具有内核持久性,即使打开该消息队列的所有进程都执行了 mq_close,消息队列的引用计数已经变为 0,但只要不显式的调用 mq_unlink 函数,该队列及队列上的消息依然存在。要想销毁队列,必须要调用 mq_unlink 函数,其接口定义如下:
#include <mqueue.h>
int mq_unlink(const char *name);
消息队列的属性
首先我们来看一下消息队列的结构体是如何定义的:
struct mq_attr {
long mq_flags;
long mq_maxmsq;
long mq_msgsize;
long mq_curmsgs;
}
这个结构体定义在 <mqueue.h> 文件中,其中:
- mq_flags:0 或设置了 O_NONBLOCK
- mq_maxmsg:消息队列中的最大消息个数
- mq_msgsize:单条消息允许的最大字节数
- mq_curmsgs:消息队列当前的消息个数
如果调用 mq_open 函数创建消息队列时,第四个参数为 NULL,那么将使用默认属性,使用 mq_getattr 函数可查看消息队列的属性,其接口定义如下:
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
使用 mq_setattr 函数可以修改消息队列的 mq_flags 属性,其接口定义如下:
#include <mqueue.h>
int mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
发送消息
#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
第三个参数 msg_len 表示消息体的长度,长度为 0 也是合法的,最大不能超过 mq_msgsize。如果消息体太大,则会返回失败,并设置 errno 为 EMSGSIZE。
第四个参数为消息的优先级,是一个非负的整数,数字越大优先级越高,在 linux 中,优先级的最大上限为32768。
接收消息
#include <mqueue.h>
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
对于 POSIX 消息队列来说,最先被取走的总是到达的消息中优先级最高的一个,如果有多个消息优先级相同,那么就取走最先到达的一个。
第二个参数 msg_ptr 用于存放消息体的内存缓冲区的地址。第三个参数 msg_len 是指向缓冲区的大小(必须大于等于属性值 mq_msgsize 的值,否则返回 EMSGSIZE )。 第四个参数为消息的优先级,如果 msg_prio 不为 NULL,那么接收到的消息优先级会被复制到prio指向的位置,如果第四个参数为 NULL,则表示不关系该消息体的优先级。