消息队列
RT-Thread 消息队列服务详解
消息队列 (Message Queue) 是另一种常用的线程间通信机制,它是邮箱的扩展,可以应用于多种场景,例如线程间的消息交换、串口接收不定长数据等。
1. 消息队列的工作机制
消息队列允许线程或中断服务例程发送不定长度的消息,并将其缓存在消息队列自身的内存空间中。其他线程可以从消息队列中读取消息。当消息队列为空时,读取线程可以选择挂起等待。当有新消息到达时,挂起的线程会被唤醒以接收和处理消息。消息队列是一种异步通信方式。

如上图所示,线程或中断服务例程可以将一条或多条消息放入消息队列。同样,一个或多个线程可以从消息队列中获取消息。当多个消息被发送到消息队列时,通常遵循先进先出 (FIFO) 原则,即先进入消息队列的消息会被先传递给接收线程。
RT-Thread 的消息队列对象由多个元素组成。在消息队列被创建时,会分配消息队列控制块,其中包含消息队列名称、内存缓冲区、消息大小和队列长度等信息。同时,每个消息队列对象包含多个消息框,每个消息框可以存放一条消息。消息队列的第一个和最后一个消息框分别称为消息链表头和消息链表尾,对应于消息队列控制块中的 msg_queue_head
和 msg_queue_tail
。有些消息框可能为空,这些空闲消息框通过 msg_queue_free
形成一个空闲消息框链表。消息队列中的消息框总数即是消息队列的长度,这个长度在消息队列创建时被指定。
2. 消息队列控制块
在 RT-Thread 中,消息队列控制块是操作系统用于管理消息队列的数据结构,由结构体 struct rt_messagequeue
表示。另一种 C 表达方式 rt_mq_t
表示消息队列的句柄,其本质是一个指向 struct rt_messagequeue
结构体的指针。消息队列控制块的详细定义如下:
struct rt_messagequeue
{
struct rt_ipc_object parent;
void* msg_pool; /* 指向存放消息的缓冲区的指针 */
rt_uint16_t msg_size; /* 每个消息的长度 */
rt_uint16_t max_msgs; /* 最大能够容纳的消息数 */
rt_uint16_t entry; /* 队列中已有的消息数 */
void* msg_queue_head; /* 消息链表头 */
void* msg_queue_tail; /* 消息链表尾 */
void* msg_queue_free; /* 空闲消息链表 */
rt_list_t suspend_sender_thread; /* 发送线程的挂起等待队列 */
};
typedef struct rt_messagequeue* rt_mq_t;
rt_messagequeue
对象继承自 rt_ipc_object
,由 IPC 容器进行管理。
3. 消息队列的管理方式
消息队列控制块结构体中包含消息队列管理的关键参数。对消息队列的操作包括:创建/初始化、发送消息、接收消息以及删除/脱离消息队列。

3.1 创建和删除消息队列
3.1.1 创建动态消息队列
可以使用 rt_mq_create()
函数动态创建一个消息队列对象:
rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size,
rt_size_t max_msgs, rt_uint8_t flag);
创建消息队列时,系统首先从对象管理器中分配一个消息队列对象,然后为该消息队列分配一块内存空间,组织成空闲消息链表。内存空间大小为 [消息大小 + 消息头 (用于链表连接) 的大小] * 消息队列最大个数
。接着,系统会初始化消息队列,此时消息队列为空。rt_mq_create()
函数的参数和返回值说明如下:
参数 | 描述 |
---|---|
name |
消息队列的名称。 |
msg_size |
消息队列中一条消息的最大长度,以字节为单位。 |
max_msgs |
消息队列的最大消息数。 |
flag |
消息队列的等待方式,可以取值为 RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO 。RT_IPC_FLAG_FIFO 表示先进先出, RT_IPC_FLAG_PRIO 表示优先级调度。 |
返回值 | 描述 |
消息队列对象的句柄 |
创建成功,返回消息队列对象的句柄 (消息队列控制块指针)。 |
RT_NULL |
创建失败。 |
注意:
RT_IPC_FLAG_FIFO
属于非实时调度方式。除非应用程序非常在意先进先出,且清楚地知道所有涉及该消息队列的线程都会变为非实时线程,否则建议使用RT_IPC_FLAG_PRIO
,以确保线程的实时性。
3.1.2 删除动态消息队列
当消息队列不再使用时,应调用 rt_mq_delete()
函数删除该消息队列以释放系统资源:
rt_err_t rt_mq_delete(rt_mq_t mq);
删除消息队列时,如果有线程挂起在该消息队列的等待队列上,内核会先唤醒所有挂起的线程(线程返回错误码 -RT_ERROR
),然后释放消息队列使用的内存,最后删除消息队列对象。 rt_mq_delete()
函数的参数和返回值说