RT-Thread邮箱

举报
yd_274589494 发表于 2024/05/26 08:20:12 2024/05/26
【摘要】 @TOC 前言本篇文章开始将带大家来学习RT-Thread中的邮箱,了解RT-Thread中邮箱的概念和学习如何使用RT-Thread中的邮箱。 一、RT-Thread邮箱的概念在 RT-Thread 中,邮箱(Mailbox)是一种线程间通信的机制,它提供了一种安全可靠的方式,使得不同的线程能够进行数据传递而无需共享内存。虽然可以将邮箱简单地视为一个环形缓冲区,但它更像是一个包含了一些额外...

@TOC


前言

本篇文章开始将带大家来学习RT-Thread中的邮箱,了解RT-Thread中邮箱的概念和学习如何使用RT-Thread中的邮箱。

一、RT-Thread邮箱的概念

在 RT-Thread 中,邮箱(Mailbox)是一种线程间通信的机制,它提供了一种安全可靠的方式,使得不同的线程能够进行数据传递而无需共享内存。虽然可以将邮箱简单地视为一个环形缓冲区,但它更像是一个包含了一些额外功能的高级环形缓冲区。

以下是 RT-Thread 邮箱的主要特点和概念:

环形缓冲区:

邮箱内部维护了一个固定大小的环形缓冲区,用于存储消息。
这个环形缓冲区可以容纳多个消息,每个消息的大小可以不同。

消息传递:

线程可以向邮箱发送消息,也可以从邮箱接收消息。
发送消息时,线程将消息写入邮箱的缓冲区。
接收消息时,线程从邮箱的缓冲区中读取消息。

阻塞和非阻塞:

发送消息和接收消息操作可以是阻塞的或非阻塞的。
阻塞操作会导致线程在邮箱操作完成之前挂起,直到有足够的空间存放消息(发送)或有消息可读取(接收)。
非阻塞操作会立即返回,如果无法完成操作,则返回错误码或空消息。

同步和异步:

邮箱的发送和接收操作可以是同步的或异步的。
同步操作会导致发送线程或接收线程阻塞,直到操作完成。
异步操作会立即返回,不会等待操作完成。

安全性和可靠性:

邮箱提供了一种安全可靠的数据传递方式,不同线程之间无需共享内存。
线程不需要担心竞争条件或同步问题,因为邮箱的操作是原子的。
通过使用邮箱,RT-Thread 系统中的不同线程可以进行灵活的、安全的数据传递,这提高了系统的可维护性和可扩展性。同时,由于邮箱是基于环形缓冲区实现的,因此具有高效的内存利用率和快速的消息传递速度。

在这里插入图片描述

二、邮箱操作函数

1.创建邮箱

当在RTThread中需要使用邮箱(Mailbox)进行线程间通信时,可以使用 rt_mb_create 和 rt_mb_init 这两个函数来创建和初始化邮箱。

rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);

参数:
name:邮箱的名称,用于标识和识别该邮箱。
size:邮箱中消息队列的容量,即能够存储的消息数量。
flag:邮箱的标志,控制邮箱的行为,例如RT_IPC_FLAG_FIFO表示使用FIFO方式管理消息。
返回值:成功则返回邮箱的句柄(rt_mailbox_t类型),失败则返回NULL。

这个函数用于在运行时动态创建一个邮箱,并返回邮箱的句柄。通过这个句柄,可以在后续的代码中对邮箱进行操作,如发送和接收消息。

 rt_err_t rt_mb_init(rt_mailbox_t mb,
 const char* name,
 void* msgpool,
 rt_size_t size,
 rt_uint8_t flag)

参数:
mb:要初始化的邮箱的指针。
name:邮箱的名称,用于标识和识别该邮箱。
msgpool:消息池的指针,用于分配存储消息的内存空间。
size:邮箱中消息队列的容量,即能够存储的消息数量。
flag:邮箱的标志,控制邮箱的行为,例如RT_IPC_FLAG_FIFO表示使用FIFO方式管理消息。

返回值:成功则返回0,失败则返回错误码。
这个函数用于对一个已经存在的邮箱进行初始化,为其分配内存并设置相关参数,以便后续的操作。通常情况下,需要提前准备好存储消息的内存空间,并将其传递给 msgpool 参数。

这两个函数可以根据实际需求选择使用。rt_mb_create 适用于在运行时动态创建邮箱的情况,而 rt_mb_init 则适用于对已经存在的邮箱进行初始化。在使用这些函数时,需要注意参数的设置,以及对函数返回值的处理,以确保邮箱的创建和初始化操作能够正确完成。

2.删除邮箱

当在 RTThread 中不再需要使用邮箱时,可以使用 rt_mb_deletert_mb_detach 这两个函数来删除或分离邮箱。

  1. rt_err_t rt_mb_delete (rt_mailbox_t mb);

    • 参数:
      • mb:要删除的邮箱的句柄。
    • 返回值:成功则返回 RT_EOK(0),失败则返回错误码。

    这个函数用于删除邮箱,并释放相关资源。删除邮箱会导致任何等待在该邮箱上的线程被唤醒,并返回错误码(通常是 RT_ERROR),以指示邮箱已被删除。

  2. rt_err_t rt_mb_detach(rt_mailbox_t mb);

    • 参数:
      • mb:要分离信号量的邮箱的句柄。
    • 返回值:成功则返回 RT_EOK(0),失败则返回错误码。

    这个函数用于从邮箱中分离一个信号量。分离操作会将指定的信号量从邮箱中移除,但并不会删除邮箱本身。分离的信号量可以继续使用,而不再受限于邮箱的状态。

这两个函数在不同的场景中有不同的用途:

  • 当不再需要使用邮箱时,可以调用 rt_mb_delete 来释放邮箱占用的资源。
  • 当想要从邮箱中移除某个信号量,但保留邮箱本身时,可以使用 rt_mb_detach

在调用这两个函数时,需要确保传递正确的邮箱句柄,并适时处理返回值,以便根据需要进行后续的操作。

3.发邮件

发邮件可以使用rt_mb_send 和 rt_mb_send_wait 这两个函数来进行发邮件:

rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value)

参数:
mb:要发送消息的邮箱的句柄。
value:要发送的消息。

返回值:成功则返回 RT_EOK(0),失败则返回错误码。
这个函数用于向邮箱发送一条消息,即将一个指定的值发送到邮箱中。如果邮箱已满,发送操作将失败,并根据邮箱的状态返回相应的错误码。

rt_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout)

参数:
mb:要发送消息的邮箱的句柄。
value:要发送的消息。
timeout:等待的超时时间,单位为时钟节拍数。若设为 -1,表示永久等待,若设为 0,表示不等待。

返回值:成功则返回 RT_EOK(0),失败则返回错误码。
这个函数与 rt_mb_send 类似,但是增加了超时等待的功能。如果邮箱已满,发送操作会等待一段时间,直到超时或者成功发送消息。超时时间由参数 timeout 指定。
这两个函数允许线程向邮箱发送消息,实现线程间的消息通信。在使用这些函数时,需要注意以下几点:

对于 rt_mb_send,如果邮箱已满,发送操作将失败,线程可能会进入阻塞状态(取决于实现),直到有空间可以发送消息。

对于 rt_mb_send_wait,可以通过设置超时时间来控制发送操作的阻塞时间,以避免无限等待的情况发生。
在调用这些函数之前,需要确保传递正确的邮箱句柄,并根据函数返回值进行适当的错误处理。

4.收邮件

函数
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout)
用于从邮箱中接收消息,下面是它的详细解释:

参数:
mb:要接收消息的邮箱的句柄。
value:用于存储接收到的消息的指针。
timeout:等待的超时时间,单位为时钟节拍数。若设为 -1,表示永久等待,若设为 0,表示不等待。

返回值:成功则返回 RT_EOK(0),失败则返回错误码。
这个函数用于从邮箱中接收一条消息,即从邮箱中取出一条消息并存储在 value 指向的地址中。如果邮箱为空,则接收操作会等待一段时间,直到超时或者成功接收到消息。超时时间由参数 timeout 指定。

如果接收操作成功,函数返回 RT_EOK;如果超时或者邮箱接收过程中出现了错误,函数将返回相应的错误码。

三、邮箱的基本使用

#include <rtthread.h>

// 定义一个邮箱句柄
static rt_mailbox_t mailbox;

// 定义线程1
static void thread1_entry(void *parameter)
{
    rt_uint32_t value = 100;

    // 往邮箱中发送消息
    rt_mb_send(mailbox, value);

    // 执行其他任务
}

// 定义线程2
static void thread2_entry(void *parameter)
{
    rt_uint32_t value = 0;

    // 从邮箱中接收消息
    rt_err_t result = rt_mb_recv(mailbox, &value, RT_WAITING_FOREVER);
    if (result == RT_EOK)
    {
        // 接收成功,处理接收到的消息
        rt_kprintf("Received value: %d\n", value);
    }
    else
    {
        // 接收失败,处理错误
        rt_kprintf("Failed to receive message from mailbox, error code: %d\n", result);
    }
}

int main(void)
{
    // 创建一个邮箱,大小为1
    mailbox = rt_mb_create("mb1", 1, RT_IPC_FLAG_FIFO);
    if (mailbox == RT_NULL)
    {
        rt_kprintf("Failed to create mailbox\n");
        return -1;
    }

    // 创建线程1
    rt_thread_t thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 1024, 25, 10);
    if (thread1 != RT_NULL)
    {
        rt_thread_startup(thread1);
    }

    // 创建线程2
    rt_thread_t thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 1024, 26, 10);
    if (thread2 != RT_NULL)
    {
        rt_thread_startup(thread2);
    }

    return 0;
}

这段代码演示了如何使用 RTThread 中的邮箱进行线程间的消息传递。主要步骤如下:

创建一个邮箱:使用 rt_mb_create 函数创建一个邮箱,指定其大小和标志。

在线程1中发送消息:使用 rt_mb_send 函数向邮箱中发送一条消息。

在线程2中接收消息:使用 rt_mb_recv 函数从邮箱中接收消息,此处采用了永久等待的方式,直到成功接收到消息。

处理接收到的消息:如果接收成功,线程2将打印出接收到的消息值;否则打印接收失败的信息和错误码。

四、邮箱和消息队列的比较

邮箱(Mailbox)和消息队列(Message Queue)是实时操作系统中常用的两种线程间通信机制,它们都可以用于在不同线程之间传递数据和实现同步。

数据结构比较:
邮箱:通常实现为一个固定大小的环形缓冲区,存储在内存中。它具有固定的容量,因此一旦邮箱被填满,后续发送的消息可能会阻塞,直到有空间可用。
消息队列:通常实现为链表结构,每个节点包含一条消息。消息队列一般没有明确的容量限制,可以动态地增加或减少消息的数量,因此在理论上可以存储无限数量的消息。

数据大小:
邮箱:通常用于传递固定大小的数据块,例如4字节整数或指针。由于邮箱是固定大小的,因此每个消息的大小一般也是固定的。
消息队列:消息队列通常不限制消息的大小,可以传递各种类型和大小的数据,包括复杂的数据结构和大型数据块。

阻塞机制:
邮箱:当邮箱已满时,发送操作可能会阻塞,直到有空间可用;当邮箱为空时,接收操作可能会阻塞,直到有消息可用。
消息队列:通常可以设置阻塞和非阻塞模式。在阻塞模式下,发送和接收操作可能会等待直到消息队列中有空间或者消息可用;在非阻塞模式下,如果消息队列已满或者为空,发送和接收操作将立即返回。

适用场景:
邮箱:适用于传递固定大小的数据,特别是用于线程之间的简单同步和通信。
消息队列:适用于传递各种类型和大小的数据,特别是在需要灵活处理不同类型消息的情况下。

总结

本篇文章就讲解到这里,邮箱的操作其实和消息队列的操作是差不多的,大家可以使用邮箱和消息队列做一个对比。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。