发布时间:2026/9/7 21:07:11
RT-Thread快速入门(五)之邮箱 前言在上一篇中我们学习了消息队列——它可以传递任意大小的数据非常灵活。但它的内部涉及链表操作和memcpy如果只是传递一些很小的数据比如状态码、计数值、指针地址用消息队列就有点杀鸡用牛刀了。RT-Thread 提供了另一种轻量级的线程间通信机制——邮箱Mailbox。一句话总结传递小数据用邮箱传递大数据用消息队列。目录前言一、邮箱的特性1.1 邮箱的本质1.2 核心特性1.3 邮箱 vs 消息队列二、邮箱操作 API2.1 创建/初始化2.2 删除/脱离2.3 发送邮件2.4 接收邮件三、完整示例四、邮箱的使用技巧4.1 通过邮箱传递指针4.2 邮箱满时的阻塞发送五、邮箱 vs 消息队列 选型指南六、总结一、邮箱的特性1.1 邮箱的本质邮箱的底层实现是环形缓冲区Ring Buffer而非消息队列的链表结构写入端 读出端 ┌──────────┐ ┌────┬────┬────┬────┐ ┌──────────┐ │ 线程A │───→│ │ │ │ │───→│ 线程B │ ├──────────┤ │ ██ │ ██ │ ██ │ ██ │ ├──────────┤ │ 线程B │───→│ │ │ │ │───→│ ISR │ ├──────────┤ └────┴────┴────┴────┘ └──────────┘ │ ISR │───→ 环形缓冲区 └──────────┘ 每格 4 字节1.2 核心特性特性说明邮件大小每封邮件固定4 字节32 位系统上为一个rt_uint32_t底层结构环形缓冲区数组比链表的消息队列更高效存储能力可缓存一定数量的邮件发送内容任意 32 位数据可以是整数值也可以是指针阻塞访问空时读可阻塞满时写可阻塞超时机制支持超时等待1.3 邮箱 vs 消息队列对比项邮箱Mailbox消息队列Message Queue数据大小固定 4 字节任意大小内部实现环形缓冲区数组链表 memcpy效率⭐⭐⭐⭐⭐更高⭐⭐⭐ 中等内存开销小每个邮件 4 字节较大消息块 链表指针适用场景传递小数值 / 指针传递大数据 / 结构体紧急消息不支持rt_mb_urgent()支持rt_mq_urgent()选型口诀传数值用邮箱传结构体用消息队列。二、邮箱操作 API2.1 创建/初始化动态创建 —rt_mb_creatert_mailbox_t rt_mb_create(const char* name, rt_size_t size, // 邮箱容量邮件个数 rt_uint8_t flag); // RT_IPC_FLAG_FIFO / RT_IPC_FLAG_PRIO返回值成功返回邮箱句柄失败返回RT_NULL邮件缓冲区由函数内部动态分配// 示例创建可容纳 5 封邮件的邮箱 rt_mailbox_t mb rt_mb_create(mbox, 5, RT_IPC_FLAG_FIFO); if (mb RT_NULL) rt_kprintf(Failed to create mailbox!\n);静态初始化 —rt_mb_initrt_err_t rt_mb_init(rt_mailbox_t mb, // 邮箱句柄 const char* name, void* msgpool, // 预分配的缓冲区 rt_size_t size, // 邮箱容量邮件个数 rt_uint8_t flag);返回值成功返回RT_EOK注意size是邮件个数不是字节数。每个邮件占 4 字节所以size 缓冲区总字节 / 4// 示例静态初始化缓冲区 20 字节 → 可容纳 5 封邮件 static rt_uint32_t mb_pool[5]; static struct rt_mailbox mb; ​ rt_mb_init(mb, mbt, mb_pool[0], sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);2.2 删除/脱离// 删除动态创建的邮箱 rt_err_t rt_mb_delete(rt_mailbox_t mb); ​ // 脱离静态初始化的邮箱 rt_err_t rt_mb_detach(rt_mailbox_t mb);2.3 发送邮件RT-Thread 提供三个发送函数函数说明阻塞rt_mb_send(mb, value)发送普通邮件放到队尾❌ 满时返回-RT_EFULLrt_mb_send_wait(mb, value, timeout)等待发送满时阻塞✅ 满时阻塞等待/* 普通发送 */ rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value); ​ /* 等待发送 */ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout);参数value是rt_uint32_t类型即 32 位无符号整数可以传递整数值如状态码、计数值指针地址如(rt_uint32_t)ptr2.4 接收邮件rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, // 输出参数接收到的邮件值 rt_int32_t timeout);返回值RT_EOK成功 /-RT_ETIMEOUT超时 /-RT_ERROR错误三、完整示例本程序创建一个邮箱、1 个发送线程、1 个接收线程#include rtthread.h ​ /* 邮箱控制块和缓冲区静态方式 */ static struct rt_mailbox mb; static rt_uint32_t mb_pool[10]; // 可容纳 10 封邮件 ​ /* 线程句柄 */ static rt_thread_t thread1 RT_NULL; static rt_thread_t thread2 RT_NULL; ​ #define THREAD_PRIORITY 15 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 10 ​ /* 发送线程入口函数 */ static void thread1_entry(void *parameter) { rt_err_t result; int count 0; ​ while(1) { count; ​ /* 发送邮件数值自增 */ result rt_mb_send(mb, (rt_ubase_t)count); if (result ! RT_EOK) { rt_kprintf(rt_mb_send ERR\n); } else { rt_kprintf(rt_mb_send:%d\n\r, count); } ​ rt_thread_mdelay(10); } } ​ /* 接收线程入口函数 */ static void thread2_entry(void *parameter) { int val; ​ while(1) { /* 接收邮件永久等待 */ if (rt_mb_recv(mb, (rt_ubase_t *)val, RT_WAITING_FOREVER) RT_EOK) { rt_kprintf(rt_mb_recv:%d\n\r, val); } ​ rt_thread_mdelay(5); } } ​ int main(void) { rt_err_t result; ​ /* 1. 初始化邮箱 * mb_pool[10] 共 40 字节每个邮件 4 字节 * 容量 40 / 4 10 */ result rt_mb_init(mb, mbt, mb_pool[0], sizeof(mb_pool) / 4, // 邮箱容量 10 RT_IPC_FLAG_FIFO); // 先来先得 if (result ! RT_EOK) { rt_kprintf(rt_mb_init ERR\n); return -1; } ​ /* 2. 创建发送线程优先级 14较高 */ thread1 rt_thread_create(thread1, thread1_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE); if (thread1 ! RT_NULL) rt_thread_startup(thread1); ​ /* 3. 创建接收线程优先级 15 */ thread2 rt_thread_create(thread2, thread2_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (thread2 ! RT_NULL) rt_thread_startup(thread2); ​ return 0; }运行结果注意发送线程优先级更高14所以它先运行写入邮箱当邮箱满或调用mdelay(10)后挂起接收线程优先级 15才有机会读出。四、邮箱的使用技巧4.1 通过邮箱传递指针由于邮箱可以传递任意 32 位值因此也可以用来传递指针/* 发送端 */ char *message Hello RT-Thread!; rt_mb_send(mb, (rt_uint32_t)message); // 把指针地址当作邮件发送 ​ /* 接收端 */ char *msg; rt_mb_recv(mb, (rt_uint32_t *)msg, RT_WAITING_FOREVER); rt_kprintf(Received: %s\n, msg);⚠️ 确保指针指向的内存在线程生命周期内有效不要在接收方拿到地址后原始内存已被释放。4.2 邮箱满时的阻塞发送使用rt_mb_send_wait()可以在邮箱满时阻塞等待/* 如果邮箱满了等待 100 Tick约 100ms超时则返回 */ result rt_mb_send_wait(mb, 42, 100); if (result RT_EOK) rt_kprintf(Send OK\n); else if (result -RT_ETIMEOUT) rt_kprintf(Send timeout, mailbox full\n);五、邮箱 vs 消息队列 选型指南场景推荐使用原因传递数值状态码、计数值邮箱效率高内存开销小传递指针大块数据邮箱直接传 4 字节地址传递结构体中等大小消息队列消息队列支持任意大小数据传递不定长数据消息队列每个消息大小可不同需要多个数据缓冲消息队列链表结构更灵活实时性要求极高邮箱数组操作无动态内存分配六、总结知识点要点邮箱本质环形缓冲区数组每封邮件 4 字节动态创建rt_mb_create()— 内存由内核分配静态初始化rt_mb_init()— 需用户提供缓冲区容量 总字节 / 4发送邮件rt_mb_send()队尾 /rt_mb_urgent()队首紧急等待发送rt_mb_send_wait()满时阻塞接收邮件rt_mb_recv()— 支持超时等待传递内容32 位整数值 或 指针地址唤醒策略RT_IPC_FLAG_PRIO按优先级/RT_IPC_FLAG_FIFO按等待时间删除/脱离动态用rt_mb_delete()静态用rt_mb_detach()适用场景小数据数值/指针用邮箱大数据结构体用消息队列

相关新闻

2026/9/7 11:12:05

前端转大模型:Demo 跑通了,为什么权限和日志成了拦路虎?

聊《一个前端项目改成 AI 流程后,最难的部分完全变了》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做&…

2026/9/8 14:53:42

从需求拆解到可视化:完整数据分析项目实践指南

这学期我修了一门项目实训课,第三次作业的要求只有一句话:“任选一个自己感兴趣的话题,完成数据获取、清洗、分析到可视化的完整流程,提交分析报告并进行现场演示。”当时看完这句话,我脑子里全是问号:话题…

2026/9/8 14:53:42

COMSOL三维离散裂隙注浆模型:宾汉姆流体与粘度空间衰减实现

COMSOL三维离散裂隙注浆模型,是我这段时间一直在啃的方向。三维离散裂隙网络(DFN)下的注浆模拟,难点从来不是软件操作,而是怎么把“浆液在粗糙裂隙里怎么走”这件事儿,用数学和物理模型说圆。这个题目把宾汉…

2026/9/8 14:53:42

Kanass看板实战:从搭建到复盘的任务管理全流程

刚接手一个要用团队看板管理的项目时,多数人打开Kanass的第一反应是:这些卡片到底该怎么摆?哪种状态该放哪一列?是不是只要把任务从“待办”拖到“已完成”就算管好了? 我见过不少团队,工具买了、账号开了…

2026/9/8 14:53:42

CDA数据分析师证书在IT行业值不值得考?全面解析价值与应用

1. CDA数据分析师证书到底是个什么东西 先把这个证书说清楚,不然聊“有没有用”全是空中楼阁。CDA全称是Certified Data Analyst,由国际范围内多个数据领域机构联合推出的一个职业能力认证体系,分Level Ⅰ、Level Ⅱ、Level Ⅲ三个等级。国内…

2026/9/8 14:53:42

软件加密与硬件加密:嵌入式设备防抄板与固件保护实战解析

保护自家产品不被逆向、不被抄板、固件不被随意提取,是很多嵌入式工程师迟早要面对的事。网上关于“硬件加密”和“软件加密”的讨论一直挺热闹,但大部分说法都比较含糊,比如有人说“软件加密就是容易被破解,硬件加密就安全了”&a…

2026/9/8 14:48:42

MODBUS RTU协议详解:帧格式、CRC校验与现场调试避坑指南

MODBUS这名字,只要做嵌入式稍微久一点,肯定绕不开。我第一次接触这协议是在一个PLC项目里,当时拿到一串十六进制报文,对着手册一头雾水,后来真正把RTU帧格式、CRC校验、功能码这些啃透之后,才意识到这协议比…

2026/9/8 7:15:10

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/8 7:15:10

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/8 0:01:49

踩多轮坑才跑通|OpenClaw 3.1.0 双平台本地 AI 自动化搭建实操实录

🔹 工具简述 OpenClaw 是一款备受开发者与办公人群青睐的开源本地智能工具,凭借离线本地运行、可视化图形面板、全流程自主任务处理三大核心特点,积累了众多忠实用户。与普通对话类 AI 产品不同,它能够直接调用电脑的软硬件操作权…

2026/9/8 0:01:50

拒绝复杂命令行,Hermes Agent 一键包快速解锁智能办公能力

🔍前言 不少想要体验 Hermes Agent 办公能力的使用者,往往会被复杂的环境配置拦住使用脚步。手动下载匹配依赖、反复调整系统目录、处理命令行持续报错、修复权限异常、补全丢失核心文件等一系列操作,对普通使用者而言门槛较高,很…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/7 22:45:59

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…