# 7.2 POSIX 消息队列

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

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

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

```c
#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 */);
```

关闭一个消息队列：

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

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

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

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

### 消息队列特性

mq\_open 中的 attr 可以指定新创建的消息队列的特性，它是一个 mq\_attr 结构变量，定义如下：

```c
#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 在消息队列创建时就确定下来，之后便无法改变。系统也会对这两个特性有一个限制。 获取消息队列特性：

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

修改消息队列特性：

```c
#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 的状态。

### 交换消息

发送一个消息：

```c
#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 错误。 接收一个消息：

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://linuxprogramming.weijun-lin.top/7.-posix-ipc/7.2-posix-xiao-xi-dui-lie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
