本文参考引用于RT-Thread 官方文档中心
在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取,根据读取到的全局变量值执行相应的动作,达到通信协作的目的。RT-Thread 中则提供了邮箱、消息队列、信号用于线程间的通信。
RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,如下图所示,线程或中断服务例程把一封 4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。
邮箱的管理方式
邮箱控制块是一个结构体,其中含有事件相关的重要参数,在邮箱的功能实现中起重要的作用。邮箱的相关接口如下图所示,对一个邮箱的操作包含:创建 / 初始化邮箱、发送邮件、接收邮件、删除 / 脱离邮箱。
动态创建一个邮箱对象可以调用如下的函数接口:
rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);
参数 | 描述 |
---|---|
name | 邮箱名称 |
size | 邮箱容量 |
flag | 邮箱标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
返回 | —— |
RT_NULL | 创建失败 |
邮箱对象的句柄 | 创建成功 |
rt_err_t rt_mb_delete (rt_mailbox_t mb);
发送邮件
线程或者中断服务程序可以通过邮箱给其他线程发送邮件,发送邮件函数接口如下:
rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);
复制错误复制成功
发送的邮件可以是 32 位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 -RT_EFULL 的返回值。下表描述了该函数的输入参数与返回值:
参数 | 描述 |
---|---|
mb | 邮箱对象的句柄 |
value | 邮件内容 |
返回 | —— |
RT_EOK | 发送成功 |
-RT_EFULL | 邮箱已经满了 |
接收邮件
只有当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回 RT_EOK 的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。接收邮件函数接口如下:
rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);
参数 | 描述 |
---|---|
mb | 邮箱对象的句柄 |
value | 邮件内容 |
timeout | 超时时间 |
返回 | —— |
RT_EOK | 接收成功 |
-RT_ETIMEOUT | 超时 |
-RT_ERROR | 失败,返回错误 |
邮箱使用案例
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
rt_mailbox_t mb = RT_NULL;
rt_thread_t send_th = RT_NULL,rece_th = RT_NULL;
char msg1[] = "msg1";
char msg2[] = "msg2";
char msg3[] = "over";
//发送3个消息
void send_mail_thread_entry(void *parameter)
{
rt_uint32_t i = 0;
while(1){
i++;
if(i > 2){
rt_mb_send(mb, (rt_uint32_t)msg2);
}else{
rt_mb_send(mb, (rt_uint32_t)msg1);
}
if(i == 4){
rt_mb_send(mb, (rt_uint32_t)msg3);
break;
}
rt_thread_mdelay(500);
}
rt_kprintf("exit send_mail_thread_entry \n");
}
void rece_mail_thread_entry(void *parameter)
{
char *rece_mail = NULL;
while(1){
rt_mb_recv(mb, (rt_ubase_t *)&rece_mail, RT_WAITING_FOREVER);
rt_kprintf("receive the mailed content :%s\n",rece_mail);
if(rece_mail == msg3){ //如果收到“over” 则释放邮箱zi'yua
break;
}
rt_thread_mdelay(500);
}
rt_mb_delete(mb); //释放后不能再使用邮箱句柄发送邮件
rt_kprintf("exit rece_mail_thread_entry \n");
}
int main(void)
{
mb = rt_mb_create("mb_demo", 64, RT_IPC_FLAG_FIFO);
if(mb == RT_NULL){
LOG_E("rt_mb_create failed ...\n");
return RT_ENOMEM;
}
send_th = rt_thread_create("send_th", send_mail_thread_entry, NULL, 1024, 10, 5);
if(send_th == RT_NULL){
LOG_E("rt_thread_create[send_th] failed ...\n");
return RT_ENOMEM;
}
rece_th = rt_thread_create("rece_th", rece_mail_thread_entry, NULL, 1024, 10, 5);
if(rece_th == RT_NULL){
LOG_E("rt_thread_create[rece_th] failed ...\n");
return RT_ENOMEM;
}
rt_thread_startup(send_th);
rt_thread_startup(rece_th);
return RT_EOK;
}
运行结果
[2022-11-02_16:32:56:954]
[2022-11-02_16:32:56:954] \ | /
[2022-11-02_16:32:56:954]- RT - Thread Operating System
[2022-11-02_16:32:56:954] / | \ 4.0.3 build Nov 1 2022
[2022-11-02_16:32:56:970] 2006 - 2020 Copyright by rt-thread team
[2022-11-02_16:32:56:970]receive the mailed content :msg1
[2022-11-02_16:32:56:970]msh >receive the mailed content :msg1
[2022-11-02_16:32:57:971]receive the mailed content :msg2
[2022-11-02_16:32:58:476]exit send_mail_thread_entry
[2022-11-02_16:32:58:476]receive the mailed content :msg2
[2022-11-02_16:32:58:976]receive the mailed content :over
[2022-11-02_16:32:58:991]exit rece_mail_thread_entry