docs/zh-cn/api/kernel/msgqueue.md

306 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\[ [English](../../../en/api/kernel/msgqueue.md) | 简体中文 \]
# 消息队列 API
openvela 提供符合 POSIX 标准的消息队列接口,用于任务间的异步消息传递。消息队列支持优先级排序,高优先级消息优先被接收。
头文件:`#include <mqueue.h>`
## openvela 实现说明
- **中断中发送**`mq_send()` 可在中断上下文中调用,但行为不同:不检查队列大小,使用预分配消息(数量由 `PREALLOC_MQ_IRQ_MSGS` 配置)
- **通知行为差异**`mq_notify()` 即使有任务在等待接收消息,仍会发送通知信号,与 POSIX 规范不完全一致
- **超时时间**`mq_timedsend()` 和 `mq_timedreceive()` 使用基于 Epoch 的绝对时间
## 队列管理
### mq_open
```c
mqd_t mq_open(const char *mqName, int oflags, ...);
```
在调用任务和消息队列之间建立连接。成功调用后,返回的消息队列描述符可用于后续操作,直到调用 `mq_close()`
**参数**
- `mqName` 消息队列的名称。
- `oflags` 打开标志位,可以是以下的任意组合:
- `O_RDONLY` 只读。
- `O_WRONLY` 只写。
- `O_RDWR` 读写。
- `O_CREAT` 如果消息队列不存在则创建。
- `O_EXCL``O_CREAT` 配合,如果队列已存在则失败。
- `O_NONBLOCK` 非阻塞模式。
- `...` 可选参数,当使用 `O_CREAT` 时需要提供:
- `mode``mode_t`)文件权限位。当前实现中未使用,但 POSIX 要求提供。
- `attr``struct mq_attr *`)队列属性。如果为 `NULL` 则使用默认值。`mq_maxmsg` 设置最大消息数,`mq_msgsize` 设置最大消息大小。
**返回值**
成功时返回消息队列描述符(`mqd_t`),失败时返回 -1`ERROR`)并设置 `errno`
- `ENOENT` 未设置 `O_CREAT` 且指定的队列不存在。
- `EEXIST` 同时设置了 `O_CREAT``O_EXCL`,但队列已存在。
- `EINVAL` 参数无效(如 `mqName``NULL`)。
- `ENOMEM` 内存不足,无法创建队列。
- `ENFILE` 系统消息队列数量已达上限。
**POSIX 兼容性**:兼容 POSIX 同名接口。
### mq_close
```c
int mq_close(mqd_t mqdes);
```
关闭消息队列描述符,释放系统分配给该任务的资源。如果任务在该队列上注册了通知请求,通知会被取消。
**参数**
- `mqdes` 消息队列描述符。
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `EBADF` `mqdes` 不是有效的消息队列描述符。
**注意**
-`mq_send()``mq_receive()` 阻塞时调用 `mq_close()` 的行为是未定义的。
- 关闭后再次使用同一 `mqdes` 的行为是未定义的。
**POSIX 兼容性**:兼容 POSIX 同名接口。
### mq_unlink
```c
int mq_unlink(const char *mqName);
```
删除指定名称的消息队列。如果有任务仍然打开了该队列,删除会推迟到所有描述符都关闭后执行。
**参数**
- `mqName` 消息队列的名称。
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `ENOENT` 指定名称的队列不存在。
- `EINVAL` `mqName``NULL`
**POSIX 兼容性**:兼容 POSIX 同名接口。
## 消息发送
### mq_send
```c
int mq_send(mqd_t mqdes, const void *msg, size_t msglen, int prio);
```
将消息发送到消息队列。消息按优先级排序,高优先级消息排在低优先级之前。`prio` 不能超过 `MQ_PRIO_MAX`
如果队列已满且未设置 `O_NONBLOCK`,调用阻塞直到有空间可用。如果设置了 `O_NONBLOCK`,立即返回错误。
**参数**
- `mqdes` 消息队列描述符。
- `msg` 要发送的消息。
- `msglen` 消息的字节长度,不能超过 `mq_msgsize`
- `prio` 消息优先级。
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `EAGAIN` 队列已满且设置了 `O_NONBLOCK`
- `EINVAL` `msg``mqdes``NULL`,或 `prio` 无效。
- `EPERM` 消息队列未以写模式打开。
- `EMSGSIZE` `msglen` 超过队列的 `mq_msgsize`
- `EINTR` 被信号中断。
**注意**
- `mq_send()` 可在中断上下文中调用,但行为不同:不检查队列大小(总是发送),使用预分配消息(数量由 `PREALLOC_MQ_IRQ_MSGS` 配置),不分配新内存。
**POSIX 兼容性**:兼容 POSIX 同名接口。
### mq_timedsend
```c
int mq_timedsend(mqd_t mqdes, const void *msg, size_t msglen, int prio,
const struct timespec *abstime);
```
带超时的消息发送。行为与 `mq_send()` 相同,但在队列满时最多阻塞到 `abstime` 指定的绝对时间。
**参数**
- `mqdes` 消息队列描述符。
- `msg` 要发送的消息。
- `msglen` 消息的字节长度。
- `prio` 消息优先级。
- `abstime` 绝对超时时间(基于 Epoch
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `EAGAIN` 队列已满且设置了 `O_NONBLOCK`
- `EINVAL` `msg``mqdes``NULL`,或 `prio` 无效。
- `EPERM` 消息队列未以写模式打开。
- `EMSGSIZE` `msglen` 超过队列的 `mq_msgsize`
- `EINTR` 被信号中断。
- `ETIMEDOUT` 超时。
**POSIX 兼容性**:兼容 POSIX 同名接口。
## 消息接收
### mq_receive
```c
ssize_t mq_receive(mqd_t mqdes, void *msg, size_t msglen, int *prio);
```
从消息队列接收优先级最高且最早的消息。`msglen` 不能小于队列的 `mq_msgsize`,否则返回错误。
如果队列为空且未设置 `O_NONBLOCK`,调用阻塞直到有消息可用。多个等待任务中,优先级最高且等待最久的任务优先接收。
**参数**
- `mqdes` 消息队列描述符。
- `msg` 接收消息的缓冲区。
- `msglen` 缓冲区大小(字节)。
- `prio` 如果非 `NULL`,存储接收到的消息的优先级。
**返回值**
成功时返回消息长度(字节),失败时返回 -1`ERROR`)并设置 `errno`
- `EAGAIN` 队列为空且设置了 `O_NONBLOCK`
- `EPERM` 消息队列未以读模式打开。
- `EMSGSIZE` `msglen` 小于队列的 `mq_msgsize`
- `EINTR` 被信号中断。
**POSIX 兼容性**:兼容 POSIX 同名接口。
### mq_timedreceive
```c
ssize_t mq_timedreceive(mqd_t mqdes, char *msg, size_t msglen,
unsigned int *prio, const struct timespec *abstime);
```
带超时的消息接收。行为与 `mq_receive()` 相同,但在队列空时最多阻塞到 `abstime` 指定的绝对时间。
**参数**
- `mqdes` 消息队列描述符。
- `msg` 接收消息的缓冲区。
- `msglen` 缓冲区大小(字节)。
- `prio` 如果非 `NULL`,存储接收到的消息的优先级。
- `abstime` 绝对超时时间(基于 Epoch
**返回值**
成功时返回消息长度(字节),失败时返回 -1`ERROR`)并设置 `errno`
- `EAGAIN` 队列为空且设置了 `O_NONBLOCK`
- `EPERM` 消息队列未以读模式打开。
- `EMSGSIZE` `msglen` 小于队列的 `mq_msgsize`
- `EINTR` 被信号中断。
- `ETIMEDOUT` 超时。
**POSIX 兼容性**:兼容 POSIX 同名接口。
## 队列属性
### mq_setattr
```c
int mq_setattr(mqd_t mqdes, const struct mq_attr *mqStat, struct mq_attr *oldMqStat);
```
设置消息队列属性。`mq_flags` 中只有 `O_NONBLOCK` 位可以被修改。
**参数**
- `mqdes` 消息队列描述符。
- `mqStat` 新属性。
- `oldMqStat` 如果非 `NULL`,存储修改前的属性。
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `EBADF` `mqdes` 不是有效的描述符。
- `EINVAL` `mqStat``NULL`
**POSIX 兼容性**:兼容 POSIX 同名接口。
### mq_getattr
```c
int mq_getattr(mqd_t mqdes, struct mq_attr *mqStat);
```
获取消息队列的当前属性。
**参数**
- `mqdes` 消息队列描述符。
- `mqStat` 返回属性结构体:
- `mq_maxmsg` 队列中的最大消息数。
- `mq_msgsize` 最大消息大小(字节)。
- `mq_flags` 消息队列标志。
- `mq_curmsgs` 当前队列中的消息数。
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `EBADF` `mqdes` 不是有效的描述符。
- `EINVAL` `mqStat``NULL`
**POSIX 兼容性**:兼容 POSIX 同名接口。
## 通知
### mq_notify
```c
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
```
注册或取消消息到达通知。当消息到达先前为空的队列时,向注册的任务发送信号通知。
如果 `notification``NULL`,取消当前注册。通知发送后注册自动取消,需要重新注册。
**参数**
- `mqdes` 消息队列描述符。
- `notification` 通知配置,包括:
- `sigev_notify` 通知方式(应为 `SIGEV_SIGNAL`)。
- `sigev_signo` 通知使用的信号编号。
- `sigev_value` 随信号传递的值。
**返回值**
成功时返回 0失败时返回 -1`ERROR`)并设置 `errno`
- `EBADF` `mqdes` 不是有效的描述符。
- `EBUSY` 另一个任务已注册了该队列的通知。
- `EINVAL` `sigev_notify` 不是有效值,或信号编号无效。
- `ENOMEM` 内存不足。
**注意**
- **与 POSIX 的行为差异**:在 openvela 中,即使有任务正在 `mq_receive()` 上阻塞等待消息通知信号仍会发送给注册的任务。POSIX 规范要求此时不发送通知(消息直接满足等待的 `mq_receive()`)。
**POSIX 兼容性**:部分兼容 POSIX 同名接口(通知时机与 POSIX 规范有差异)。