登录/注册

rt-thread邮箱使用教程

更多

好的,这是一份 RT-Thread 邮箱(Mailbox)的使用中文教程。邮箱是 RT-Thread 中一种基本的进程间通信(IPC)机制,主要用于线程间传递短消息(一个 4 字节的整数值 或 一个指针)

核心特点:

  1. 容量固定: 邮箱在创建时就确定了其能容纳的消息数量(深度)。
  2. 异步发送 & 可选阻塞接收: 发送通常是异步的(立即返回),接收可以是阻塞或非阻塞的。
  3. 存储指针或整数值: 一条消息要么是一个 32 位(4 字节)的整数,要么是一个 指针,可以用来指向实际的数据结构。
  4. 轻量高效: 相比消息队列,邮箱更适合传递简单的控制信息或小数据块的指针。

?️ 使用步骤

  1. 包含头文件:

    #include <rtthread.h>
    #include <rtdevice.h> // 如果使用静态邮箱初始化宏,则需要包含
  2. 定义邮箱控制块 & 消息容器:

    • 动态创建:
      /* 定义一个邮箱控制块的指针 */
      static rt_mailbox_t my_mailbox;

      动态创建时,RT-Thread 内核会自动分配邮箱结构体和消息容器所需的内存。

    • 静态初始化:
      /* 定义邮箱控制块 */
      static struct rt_mailbox my_static_mbox;
      /* 定义存储消息的容器(缓冲区) */
      static rt_ubase_t mail_pool[16]; // 数组类型为 rt_ubase_t (通常是 32 位整数),大小决定了邮箱深度(这里是16条消息)

      静态初始化需要手动定义邮箱控制块和一个 rt_ubase_t 类型的数组作为消息池。

  3. 初始化/创建邮箱:

    • 动态创建(推荐方式):
      #define MAILBOX_SIZE 10 // 你想创建的邮箱能容纳的消息条数
      my_mailbox = rt_mb_create("my_mail", // 邮箱名字,方便调试 (可选)
                                MAILBOX_SIZE, // 邮箱容量(深度)
                                RT_IPC_FLAG_PRIO); // 等待队列中线程的排队方式: 优先级(RT_IPC_FLAG_PRIO)或先进先出(RT_IPC_FLAG_FIFO)
      if (my_mailbox == RT_NULL) {
          rt_kprintf("Failed to create mailbox!\n");
          // 错误处理
      }
    • 静态初始化:
      rt_err_t result;
      result = rt_mb_init(&my_static_mbox, // 指向静态邮箱控制块
                         "my_static_mail", // 邮箱名字 (可选)
                         &mail_pool[0],    // 指向消息池数组的起始地址
                         sizeof(mail_pool) / sizeof(rt_ubase_t), // 计算邮箱容量 = 数组元素个数
                         RT_IPC_FLAG_PRIO); // 排队方式
      if (result != RT_EOK) {
          rt_kprintf("Failed to init static mailbox! Code: %d\n", result);
          // 错误处理
      }
  4. 发送消息: 使用 rt_mb_send()(阻塞模式)或 rt_mb_send_wait()(带超时的阻塞)通常用于线程上下文。 对于 中断服务程序(ISR),请使用 rt_mb_send_wait(),并设置超时时间为 0 (RT_WAITING_NO),因为中断中不能挂起线程。

    rt_err_t result;
    rt_uint32_t my_int_value = 0x12345678; // 或一个指针: void *my_ptr = &some_data;
    /* 在普通线程中发送(阻塞,直到邮箱有空间或超时) */
    result = rt_mb_send_wait(my_mailbox,        // 邮箱句柄
                         (rt_ubase_t)my_int_value, // 或 (rt_ubase_t)my_ptr, 强制转换为 4 字节类型
                          RT_WAITING_FOREVER); // 等待时间:永远等待直到有空位。也可以用毫秒数设置超时 (如:10 * RT_TICK_PER_SECOND / 1000)
    // 或者使用 rt_mb_send(),它等价于 rt_mb_send_wait(mb, value, RT_WAITING_NO)
    // 即立刻返回,如果邮箱满,则返回错误
    /* 在中断服务程序(ISR)中发送(非阻塞)*/
    void MyIrqHandler(void) {
        result = rt_mb_send_wait(my_mailbox, (rt_ubase_t)my_int_value, RT_WAITING_NO);
        if (result != RT_EOK) {
            // 邮箱满,发送失败,中断中需要快速处理,通常记录错误或丢弃
        }
    }

    成功发送返回值 RT_EOK,失败返回值通常是 -RT_EFULL (邮箱已满) 或 -RT_ETIMEOUT (等待超时)。

  5. 接收消息: 通常在需要消息的线程上下文中接收。接收可以是阻塞的(等待)或非阻塞的。

    rt_err_t result;
    rt_ubase_t received_value; // 用于存储接收到的整数值或指针
    /* 阻塞接收(等待消息) */
    result = rt_mb_recv(my_mailbox,         // 邮箱句柄
                    &received_value,    // 指向接收缓冲区的指针(rt_ubase_t 类型)
                    RT_WAITING_FOREVER); // 等待时间:永远等待直到有消息。也可以设置超时时间
    // 或者使用 rt_mb_recv(),等效于 rt_mb_recv(mb, value, RT_WAITING_NO)
    if (result == RT_EOK) {
        // 成功接收到消息
        // 根据发送的内容,received_value 可能是一个整数 (rt_uint32_t) 或者一个指针 (void*)
        // 如果是指针,需要转回原类型: void *my_ptr = (void *)received_value;
        rt_kprintf("Received value: 0x%x\n", (rt_uint32_t)received_value);
    } else if (result == -RT_ETIMEOUT) {
        // 等待超时,邮箱在规定时间内无消息
    } else {
        // 其他错误,如参数错误 (result == -RT_ERROR)
    }

    成功接收返回值 RT_EOK,失败返回值通常是 -RT_ETIMEOUT (等待超时) 或 -RT_ERROR (参数错误)。邮箱空返回 -RT_ETIMEOUT(在非阻塞接收中立即返回为空)。

  6. 删除/脱离邮箱:

    • 动态创建的邮箱: 使用完毕后必须删除以防止内存泄漏。
      rt_err_t result;
      result = rt_mb_delete(my_mailbox);
      if (result == RT_EOK) {
          rt_kprintf("Mailbox deleted.\n");
      }
    • 静态初始化的邮箱: 使用 rt_mb_detach() 将其从内核对象管理器中脱离(控制块和消息池内存由用户管理)。
      rt_err_t result;
      result = rt_mb_detach(&my_static_mbox);
      if (result == RT_EOK) {
          rt_kprintf("Static mailbox detached.\n");
      }

? 示例代码:线程1通过邮箱向线程2发送计数值

#include <rtthread.h>

#define MAILBOX_SIZE 5
static rt_mailbox_t count_mailbox;

static void thread1_entry(void *parameter) {
    rt_uint32_t count = 0;
    while (1) {
        // 每 500ms 发送一次计数
        rt_thread_mdelay(500);
        if (rt_mb_send(count_mailbox, count) != RT_EOK) {
            rt_kprintf("Thread1: Mailbox full! Count %d dropped.\n", count);
        } else {
            rt_kprintf("Thread1: Sent count: %d\n", count);
        }
        count++;
    }
}

static void thread2_entry(void *parameter) {
    rt_ubase_t recv_value;
    while (1) {
        // 阻塞等待计数邮箱的消息
        if (rt_mb_recv(count_mailbox, &recv_value, RT_WAITING_FOREVER) == RT_EOK) {
            rt_kprintf("Thread2: Received count: %d\n", (int)recv_value);
        }
    }
}

int mailbox_sample(void) {
    // 动态创建邮箱
    count_mailbox = rt_mb_create("cnt_mbox", MAILBOX_SIZE, RT_IPC_FLAG_FIFO);
    if (count_mailbox == RT_NULL) {
        rt_kprintf("Failed to create count mailbox!\n");
        return -1;
    }

    // 创建发送线程
    rt_thread_t tid1 = rt_thread_create("thread1",
                                      thread1_entry,
                                      RT_NULL,
                                      512, // 栈大小
                                      25,  // 优先级 (数字小优先级高)
                                      10); // 时间片
    if (tid1 != RT_NULL) rt_thread_startup(tid1);

    // 创建接收线程
    rt_thread_t tid2 = rt_thread_create("thread2",
                                      thread2_entry,
                                      RT_NULL,
                                      512,
                                      26, // 优先级比 thread1 低
                                      10);
    if (tid2 != RT_NULL) rt_thread_startup(tid2);

    return RT_EOK;
}
/* 可导出到 msh 命令 */
MSH_CMD_EXPORT(mailbox_sample, Run mailbox sample);

? 关键注意事项

  1. 容量与溢出: 务必为邮箱设置合适的容量。发送前检查返回值(特别是使用 rt_mb_send()),处理邮箱满的情况(如等待、记录错误或丢弃消息)。避免未处理的溢出。
  2. 指针传递:
    • 接收线程在接收到指针后才有权限访问该指针指向的内存。
    • 通常 发送线程负责分配内存(如使用 rt_malloc),接收线程负责释放内存(使用 rt_free),避免内存泄漏或非法访问。
    • 确保指针指向的内存在传递期间是有效的(不被释放或覆盖)。如果需要长期保存,应进行深拷贝。
  3. 等待与阻塞:
    • 使用 RT_WAITING_FOREVER 要小心,需确保逻辑上一定会发生对方线程的动作(发送/接收),否则可能导致线程永久挂起。
    • 在关键任务线程或优先级高的线程上尽量使用带超时的接收/发送,避免因邮箱空/满而导致高优先级任务被无限期阻塞(低优先级任务占用了邮箱),防止优先级反转。
  4. 中断发送: 务必在中断服务例程(ISR)中使用 rt_mb_send_wait() 并设置超时时间为 RT_WAITING_NO(0)。
  5. 任务同步: 邮箱不仅用于传递数据,也常用于传递信号或事件,0x00000001这样的数值即可代表“任务完成”这类事件。
  6. 选择正确机制: 如果你的消息长度经常超过 4 字节(指针大小不计算在内,指针本身才4字节),或者传输的数据量较大且频繁,应该考虑使用 消息队列(Message Queue)
  7. 资源释放: 动态创建的邮箱 (rt_mb_create) 务必在使用完毕后删除 (rt_mb_delete)。静态初始化的邮箱 (rt_mb_init) 在不需要时脱离 (rt_mb_detach)

? 通过理解以上概念和步骤,你可以在 RT-Thread 应用程序中有效地使用邮箱来实现线程间简单而高效的通信。祝你开发顺利!?

RT-Thread v5.0.2 发布

RT-Thread 代码仓库地址: ●  https://github.com/RT-Thread/rt-thread

2023-10-10 18:45:01

基于RT-Thread Studio学习

前期准备:从官网下载 RT-Thread Studio,弄个账号登陆,开启rt-thread学习之旅。

2023-05-15 11:00:38

RT-Thread学习笔记 RT-Thread的架构概述

RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread

2022-07-09 11:27:42

RT-Thread文档_RT-Thread SMP 介绍与移植

RT-Thread文档_RT-Thread SMP 介绍与移植

资料下载 刘燕 2023-02-22 18:31:34

RT-Thread文档_RT-Thread 潘多拉 STM32L475 上手指南

RT-Thread文档_RT-Thread 潘多拉 STM32L475 上手指南

资料下载 佚名 2023-02-22 18:23:17

RT-Thread文档_RT-Thread 简介

RT-Thread文档_RT-Thread 简介

资料下载 张览秀 2023-02-22 18:22:24

RT-Thread AI kit开源:轻松实现一键部署AI模型至 RT-Thread

RT-AK 是 RT-Thread 团队为 RT-Thread 实时操作系统所开发的 AI 套件,能够一键将 AI 模型部署到

资料下载 李伟 2022-01-25 18:18:17

嵌入式RT-Thread应用与开发

嵌入式RT-Thread应用与开发说明。

资料下载 姚小熊27 2021-04-06 14:12:00

RT-Thread记录(二、RT-Thread内核启动流程)

在前面我们RT-Thread Studio工程基础之上讲一讲RT-Thread内核启动流程.

2022-06-20 00:30:33

RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍

RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍             审核编辑:彭静

2022-05-27 16:52:41

RT-Thread全球技术大会:RT-Thread测试用例集合案例

RT-Thread全球技术大会:RT-Thread测试用例集合案例           审核编辑:彭静

2022-05-27 16:34:46

RT-Thread全球技术大会:在RT-Thread上编写测试用例

RT-Thread全球技术大会:在RT-Thread上编写测试用例           审核编辑:彭静

2022-05-27 16:28:36

RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制

RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制               审核编辑:彭静

2022-05-27 14:49:12

RT-Thread全球技术大会:RT-Thread Smart更好的兼容Linux生态

RT-Thread全球技术大会:RT-Thread Smart更好的兼容Linux生态             审核编辑:彭静

2022-05-27 14:31:32

RT-Thread全球技术大会:萤石研发团队使用RT-Thread的技术挑战

RT-Thread全球技术大会:研发团队使用RT-Thread的技术挑战         审核编辑:彭静

2022-05-27 11:36:17

7天热门专题 换一换
相关标签