返回 IPC 通信机制 HRTOS / SYSTEM MODULES / IPC
HRTOS · INTER-PROCESS COMMUNICATION

消息队列 Message Queue / FIFO Data Exchange

面向任务间有序数据传递的通信机制。通过固定容量的队列缓存数据, 将数据生产与数据处理分离,为嵌入式应用中的异步任务协作提供基础。

FIFO 数据顺序 环形缓冲区 任务间通信 阻塞 / 非阻塞接口
IPC / 04
OVERVIEW / 01

让数据流与任务执行解耦

生产者任务将数据写入队列,消费者任务按队列顺序读取数据。 双方通过队列交换信息,而不必直接依赖对方的执行时机。

HRTOS 当前展示的消息队列接口以 u8 为数据单位。 多字节数据、结构体或业务消息,需要由应用层自行定义编码、拆分和重组规则。

FIFO
有序传递 按队列规则组织数据
BUF
有限缓存 容量由调用方提供的缓冲区决定
IPC
任务解耦 连接数据生产端与消费端
01

核心接口

API Reference · 消息队列操作入口

消息队列通过初始化、发送、接收和清理接口管理数据。 以下为本页对应的接口声明;实际参数、类型定义及返回值语义应与项目当前版本的头文件保持一致。

消息队列 API C / HRTOS
void os_msgq_init(os_msgq_t *q, u8 *buf, u8 size);
char os_msgq_send(os_msgq_t *q, u8 obj, u8 data, char nonblock);
char os_msgq_recv(os_msgq_t *q, u8 obj, u8 *data, char nonblock);
void os_msgq_clear(os_msgq_t *q);
  • os_msgq_init():初始化队列对象,并关联调用方提供的缓冲区。
  • os_msgq_send():尝试向队列写入一个字节的数据。
  • os_msgq_recv():尝试从队列读取一个字节的数据。
  • os_msgq_clear():清理队列状态,具体重置范围以实现为准。
接口使用前须确认
obj 的具体用途、nonblock 的取值约定、失败返回值及等待机制, 均应以当前版本的头文件和源码为准。不要仅凭函数名称推断行为。
02

数据结构与缓冲区

Data Structure · 固定容量的队列管理

环形缓冲区是 FIFO 队列常见的实现方式。读写位置沿缓冲区循环移动, 可以在固定存储空间内持续复用已有位置,避免每次读取后整体搬移数据。

概念示意 · 非源码声明 RING BUFFER
缓冲区:[  A  ][  B  ][  C  ][    ][    ]
           ↑读                  ↑写

读取一个单元 → 读位置向前移动
写入一个单元 → 写位置向前移动
到达末尾     → 按环形规则回到起始位置

环形队列通常需要维护缓冲区地址、容量以及读写位置等状态。 上面的示意图用于解释基本原理,不代表 os_msgq_t 的真实字段布局。

  • 缓冲区地址:指向实际存储数据的内存区域。
  • 队列容量:决定最多能够暂存多少数据单元。
  • 读写状态:用于确定下一次读取或写入的位置。
  • 队列状态:实现可能通过计数或其他状态表示空队列与满队列。
资源管理注意事项
缓冲区由调用方提供时,应确保其生命周期覆盖队列的使用周期, 并保证实际存储空间与初始化容量相匹配。不要在队列仍被使用时随意释放或复用该内存。
03

发送流程

Producer · os_msgq_send()

发送操作的核心是检查队列是否具备写入条件。 如果队列有可用空间,数据可以写入缓冲区;如果队列已满,则需要根据接口约定处理本次操作。

STEP 01 检查队列状态

判断当前是否存在可用写入空间。

STEP 02 处理发送请求

有空间则写入;无空间时按调用模式处理。

STEP 03 更新与返回

按实现更新队列状态,并向调用方返回结果。

发送逻辑 · 概念示意 SEND PATH
发送请求
    │
    ▼
判断是否有可用空间
    ├─ 有空间 → 写入数据 → 更新队列状态
    └─ 无空间 → 按接口约定返回或处理等待

当消费者因队列暂时为空而等待时,生产者写入数据可能与消费者的后续运行产生关联。 实际唤醒条件和调度行为,应根据 HRTOS 当前的队列实现与内核等待机制确认。

04

接收流程

Consumer · os_msgq_recv()

接收操作需要判断队列中是否存在可读取的数据。 队列非空时,读取一个数据单元并更新相关状态;队列为空时,则依据接口约定处理当前请求。

STEP 01 检查队列状态

判断当前是否有数据可供读取。

STEP 02 读取数据

存在数据时取出一个字节。

STEP 03 更新与返回

按实现更新队列状态,并报告操作结果。

接收逻辑 · 概念示意 RECEIVE PATH
接收请求
    │
    ▼
判断是否存在可读取数据
    ├─ 有数据 → 读取数据 → 更新队列状态
    └─ 无数据 → 按接口约定返回或处理等待

消费者取走数据后,队列可能重新具备写入条件。 如果有发送方正在等待队列空间,其后续处理取决于具体实现。

05

阻塞与非阻塞机制

Execution Behavior · 调用无法立即完成时

阻塞与非阻塞描述的是操作暂时无法完成时的处理方式。 非阻塞调用通常不等待条件满足;阻塞调用则可能让当前任务等待,之后再由内核机制安排继续执行。

非阻塞调用

适用于调用方需要及时获得操作结果、不能在当前路径等待的场景。 调用方应检查返回值,并自行决定失败后的处理策略。

阻塞调用

适用于允许任务等待数据或空间的场景。 是否进入等待、如何恢复运行以及失败条件,均由接口与内核实现决定。

参数说明 · 需核对当前版本 nonblock
nonblock = 1  → 非阻塞调用(按现有接口说明)
nonblock = 0  → 阻塞调用(按现有接口说明)
不要忽略等待语义
阻塞接口的执行时间不仅取决于函数本身,还可能受队列状态、任务调度和等待机制影响。 如果应用对响应时间有严格要求,应结合当前内核实现分析实际执行路径。
06

返回值与错误处理

Return Status · 明确处理每一次调用结果

发送和接收接口返回 char 类型的状态值。 应用程序应依据当前版本定义判断操作是否成功,不应假设每次调用都能完成数据传递。

既有文档中的返回值约定 VERIFY AGAINST SOURCE
1   成功
0   操作未成功或未完成
-1  等待失败
发布前需要核对
上述数值来自原页面描述,尚不能仅凭函数签名确认其与当前实现完全一致。 正式文档应以当前头文件、返回路径和编译器类型定义为准; 尤其需要确认 char 是否为有符号类型,以及 -1 是否是实际支持的返回值。

建议调用方为发送失败、接收暂不可用和等待失败分别设计处理策略, 并避免在错误发生时无条件重复调用,以免形成高频轮询或影响其他任务的执行。

07

设计特性与使用边界

Design Considerations · 从接口走向工程应用
  • FIFO 数据组织:适用于需要按顺序处理数据的任务协作场景。
  • 固定容量缓存:队列可暂存的数据量受缓冲区容量限制,应结合生产速率和消费速率设计。
  • 任务解耦:生产者不必直接调用消费者的业务逻辑,双方可通过队列交换数据。
  • 数据单位明确:当前接口以单个 u8 为传递单位,复杂消息需要应用层协议。
  • 并发访问约束:多个任务共同访问队列时,应遵守接口规定的同步与调用约束。
中断上下文注意事项
不要在中断服务程序中直接调用可能阻塞的接口。 如果需要从 ISR 向任务传递数据,应先确认当前版本是否支持相应的中断上下文调用, 再选择符合实现约束的处理方式。
08

典型应用场景

Application Patterns · 常见任务协作模型

生产者与消费者

采集任务负责产生数据,处理任务按顺序读取并完成后续业务。

驱动与应用解耦

将驱动侧获得的数据交给应用任务处理,降低执行流程之间的直接依赖。

后台数据处理

将待处理的字节数据暂存在队列中,由后台任务分批读取。

异步数据缓冲

在数据产生与处理速度不一致时提供有限缓冲,但不能替代容量规划。

队列容量设计应考虑数据突发量、消费者处理速度、允许的积压时间以及队列满时的业务策略。 如果生产速度长期高于消费速度,单纯增加缓冲区只能延缓队列耗尽,不能消除持续积压。

09

消息队列与其他 IPC 机制

IPC Selection · 根据通信目标选择机制

不同 IPC 机制解决的问题并不相同。选择时应优先考虑应用需要传递的是数据、状态、同步信号,还是对共享资源的访问权。

  • 消息队列:用于按队列规则缓存和传递数据。
  • 邮箱(Mailbox):用于邮箱接口所定义的消息传递方式,具体容量和覆盖规则以实现为准。
  • 事件(Event):用于表达事件或条件状态,适合任务根据事件进行协作。
  • 信号量(Semaphore):通常用于同步或资源计数,具体语义由实现决定。
  • 互斥量(Mutex):用于协调对共享资源的互斥访问,具体所有权与等待规则取决于实现。
选型原则
需要缓存一串有序数据时,优先评估消息队列; 只需要通知某个条件发生时,评估事件机制; 需要保护共享资源时,则应考虑与资源访问语义匹配的同步机制。