0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

RT-Thread的邮箱服务,包括邮箱工作机制、工作管理方式以及应用示例

RTThread物联网操作系统 来源:lp 2019-03-28 09:38 次阅读

前言

前面讲了RT-Thread信号量、互斥量以及事件集这些都是线程间的同步方式。在我们进行实际的项目开发的时候,经常会涉及到一个线程更新某个全局变量值,然后另外一个线程去读取这个全局变量值,根据这个全局变量值的不同而去执行不同的操作,在RT-Thread 中则提供了更多的工具帮助在不同的线程中间传递信息,包括邮箱、消息队列、信号用于线程间的通信方式。本文将RT-Thread的邮箱服务,包括邮箱工作机制、工作管理方式以及应用示例,基于潘多拉开发板进行实验,单片机STM32L475VET6。

一、邮箱的工作机制

RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,如下图所示,线程或中断服务例程把一封 4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。

邮箱工作示意图(来源RT-Thread编程指南)

(1)非阻塞方式的邮件发送过程能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为 0 时,邮件收取过程将变成阻塞方式。在这类情况下,只能由线程进行邮件的收取。

(2)当一个线程向邮箱发送邮件时,如果邮箱没满,将把邮件复制到邮箱中。如果邮箱已经满了,发送线程可以设置超时时间,选择等待挂起或直接返回 RT_EFULL。如果发送线程选择挂起等待,那么当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送。

(3)当一个线程从邮箱中接收邮件时,如果邮箱是空的,接收线程可以选择是否等待挂起直到收到新的邮件而唤醒,或可以设置超时时间。当达到设置的超时时间,邮箱依然未收到邮件时,这个选择超时等待的线程将被唤醒并返回 RT_ETIMEOUT。如果邮箱中存在邮件,那么接收线程将复制邮箱中的 4 个字节邮件到接收缓存中。

二、邮箱的相关函数

1、创建动态邮箱函数:创建邮箱对象时会先从对象管理器中分配一个邮箱对象,然后给邮箱动态分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4 字节)与邮箱容量的乘积,接着初始化接收邮件数目和发送邮件在邮箱中的偏移量,动态创建一个邮箱对象可以调用如下的函数接口:

1rt_mailbox_trt_mb_create(constchar*name,rt_size_tsize,rt_uint8_tflag);

(1)入口参数:name:邮箱名称。size:邮箱容量。flag:邮箱标志,它可以取如下数值:RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO(2)返回值:RT_NULL:创建失败。邮箱对象的句柄:创建成功。

2、删除动态邮箱函数:当用 rt_mb_create() 创建的邮箱不再被使用时,应该删除它来释放相应的系统资源,一旦操作完成,邮箱将被永久性的删除。删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是 RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象。删除邮箱的函数接口如下:

1rt_err_trt_mb_delete(rt_mailbox_tmb);

(1)入口参数:

mb:要删除的邮箱对象的句柄。

(2)返回值:

RT_EOK:成功。

3、创建静态邮箱函数:这里所说的创建静态邮箱和《RT-Thread编程指南》所讲的初始化邮箱是一样的,跟动态创建邮箱类似,只是初始化邮箱用于静态邮箱对象的初始化。与创建邮箱不同的是,静态邮箱对象的内存是在系统编译时由编译器分配的,一般放于读写数据段或未初始化数据段中,其余的初始化工作与创建邮箱时相同。初始化邮箱时,该函数接口需要获得用户已经申请获得的邮箱对象控制块,缓冲区的指针,以及邮箱名称和邮箱容量(能够存储的邮件数)。函数接口如下:

1rt_err_trt_mb_init(rt_mailbox_tmb,2constchar*name,3void*msgpool,4rt_size_tsize,5rt_uint8_tflag);

(1)入口参数:

mb:邮箱对象的句柄。name:邮箱名称。msgpool:缓冲区指针。size:邮箱容量。flag:

邮箱标志,它可以取如下数值:RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO

(2)返回值:

RT_EOK:成功。

注意:这里的 size 参数指定的是邮箱的容量,即如果 msgpool 指向的缓冲区的字节数是 N,那么邮箱容量应该是 N/4。

4、删除静态邮箱函数:这里所说的删除静态邮箱和《RT-Thread编程指南》所讲的脱离邮箱是一样的,脱离邮箱将把静态初始化的邮箱对象从内核对象管理器中脱离,内核先唤醒所有挂在该邮箱上的线程(线程获得返回值是 RT_ERROR),然后将该邮箱对象从内核对象管理器中脱离。脱离邮箱使用下面的接口

1rt_err_trt_mb_detach(rt_mailbox_tmb);

(1)入口参数:mb:邮箱对象的句柄。

(2)返回值:RT_EOK:成功。

5、发送邮件函数:线程或者中断服务程序可以通过邮箱给其他线程发送邮件,发送的邮件可以是 32 位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 RT_EFULL的返回值。函数接口如下:

1rt_err_trt_mb_send(rt_mailbox_tmb,rt_uint32_tvalue);

(1)入口参数:mb:邮箱对象的句柄。value:邮件内容。

(2)返回值:RT_EOK:发送成功。RT_EFULL:邮箱已经满了。

6、等待方式发送邮件函数:用户也可以通过如下的函数接口向指定邮箱发送邮件:

1rt_err_trt_mb_send_wait(rt_mailbox_tmb,2rt_uint32_tvalue,3rt_int32_ttimeout);

rt_mb_send_wait()与 rt_mb_send() 的区别在于有等待时间,如果邮箱已经满了,那么发送线程将根据设定的 timeout 参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。

(1)入口参数:mb:邮箱对象的句柄。value:邮件内容。timeout:超时时间。

(2)返回值:RT_EOK:发送成功。RT_ETIMEOUT:超时。RT_ERROR:失败,返回错误。

7、接收邮件函数:接收邮件时,接收者需指定接收邮件的邮箱句柄,并指定接收到的邮件存放位置以及最多能够等待的超时时间。接收邮件函数接口如下:

1rt_err_trt_mb_recv(rt_mailbox_tmb,rt_uint32_t*value,rt_int32_ttimeout);

(1)入口参数:mb:邮箱对象的句柄。value:邮件内容。timeout:超时时间。

(2)返回值:RT_EOK:发送成功。RT_ETIMEOUT:超时。RT_ERROR:失败,返回错误。

三、基于STM32的邮箱示例

光说不练都是假把式,那么接下来我们进行RT-Thread的邮箱实验,采用RTT&正点原子联合出品潘多拉开发板,基于STM32。创建一个邮箱,两个线程,其中一个线程用于发送邮件,另外一个线程由于接收邮件。通过按下不同按键发送不同的邮件内容,根据读取到右邮件内容执行不同操作。当读取到内容为KEY0按下时点亮RGB红灯,其他熄灭,当读取到内容为KEY1按下时点亮RGB蓝灯,其他熄灭,当读取到内容为KEY0按下时点亮RGB绿灯,其他熄灭。

1、实现代码:

1#include"rtthread.h" 2#include"string.h" 3#include"mailbox_app.h" 4#include"led.h" 5#include"key.h" 6 7 8/*线程句柄*/ 9staticrt_thread_tthread1=RT_NULL; 10staticrt_thread_tthread2=RT_NULL; 11 12/*邮箱句柄*/ 13staticrt_mailbox_tmailbox1=RT_NULL; 14 15 16charmailbox_msg_key0_press[]="mailbox_msg_key0_press"; 17charmailbox_msg_key1_press[]="mailbox_msg_key1_press"; 18charmailbox_msg_key2_press[]="mailbox_msg_key2_press"; 19 20 21/************************************************************** 22函数名称:thread1_recv_mailbox_msg 23函数功能:线程1入口函数,用于接收邮件 24输入参数:parameter:入口参数 25返回值:无 26备注:无 27**************************************************************/ 28voidthread1_recv_mailbox_msg(void*parameter) 29{ 30char*mb_msg; 31 32while(1) 33{ 34if(rt_mb_recv(mailbox1,(rt_uint32_t*)&mb_msg,RT_WAITING_FOREVER)==RT_EOK) 35{ 36rt_kprintf("recvmb_msg:%s ",mb_msg); 37 38if(0==strcmp(mb_msg,"mailbox_msg_key0_press")) 39{ 40LED_R(0); 41LED_B(1); 42LED_G(1); 43} 44elseif(0==strcmp(mb_msg,"mailbox_msg_key1_press")) 45{ 46LED_R(1); 47LED_B(0); 48LED_G(1); 49} 50elseif(0==strcmp(mb_msg,"mailbox_msg_key2_press")) 51{ 52LED_R(1); 53LED_B(1); 54LED_G(0); 55} 56} 57rt_thread_mdelay(1); 58} 59} 60 61/************************************************************** 62函数名称:thread2_send_mailbox_msg 63函数功能:线程2入口函数,用于发送邮件 64输入参数:parameter:入口参数 65返回值:无 66备注:无 67**************************************************************/ 68voidthread2_send_mailbox_msg(void*parameter) 69{ 70u8key; 71 72while(1) 73{ 74key=key_scan(0); 75 76if(key==KEY0_PRES) 77{ 78rt_mb_send(mailbox1,(rt_uint32_t)&mailbox_msg_key0_press); 79} 80elseif(key==KEY1_PRES) 81{ 82rt_mb_send(mailbox1,(rt_uint32_t)&mailbox_msg_key1_press); 83} 84elseif(key==KEY2_PRES) 85{ 86rt_mb_send(mailbox1,(rt_uint32_t)&mailbox_msg_key2_press); 87} 88 89rt_thread_mdelay(1); 90} 91} 92 93 94voidrtthread_mailbox_test(void) 95{ 96mailbox1=rt_mb_create("mailbox1",12,RT_IPC_FLAG_FIFO);/*FIFO模式*/ 97 98if(mailbox1!=RT_NULL) 99{100rt_kprintf("RT-Threadcreatemailboxsuccessful ");101}102else103{104rt_kprintf("RT-Threadcreatemailboxfailed ");105return;106}107108thread1=rt_thread_create("thread1",109thread1_recv_mailbox_msg,110NULL,111512,1123,11320);114115if(thread1!=RT_NULL)116{117rt_thread_startup(thread1);;118}119else120{121rt_kprintf("createthread1failed ");122return;123}124125thread2=rt_thread_create("thread2",126thread2_send_mailbox_msg,127NULL,1281024,1292,13020);131132if(thread2!=RT_NULL)133{134rt_thread_startup(thread2);;135}136else137{138rt_kprintf("createthread2failed ");139return;140}141}

2、观察FinSH和执行效果:

(1)输入list_mailbox,可以看到由哪些邮箱,邮箱的容量以及当前挂起等待邮箱内容的线程。

(2)按下KEY0,打印如下邮件的内容,同时RGB红灯亮。

(3)按下KEY1,打印如下邮件内容,同时RGB蓝灯亮。

(4)按下KEY2,打印如下邮件内容,同时RGB绿灯亮。

四、邮箱的使用场合及技巧

邮箱是一种简单的线程间消息传递方式,特点是开销比较低,效率较高。在 RT-Thread 操作系统的实现中能够一次传递一个 4 字节大小的邮件,并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数 (邮件数由创建、初始化邮箱时指定的容量决定)。邮箱中一封邮件的最大长度是 4 字节,所以邮箱能够用于不超过 4 字节的消息传递。

由于在 32 系统上 4 字节的内容恰好可以放置一个指针,因此当需要在线程间传递比较大的消息时,可以把指向一个缓冲区的指针作为邮件发送到邮箱中,即邮箱也可以传递指针,例如:

1structmsg2{3rt_uint8_t*data_ptr;4rt_uint32_tdata_size;5};

对于这样一个消息结构体,其中包含了指向数据的指针data_ptr和数据块长度的变量 data_size。当一个线程需要把这个消息发送给另外一个线程时,可以采用如下的操作:

1structmsg*msg_ptr;23msg_ptr=(structmsg*)rt_malloc(sizeof(structmsg));4msg_ptr->data_ptr=...;/*指向相应的数据块地址*/5msg_ptr->data_size=len;/*数据块的长度*/67/*发送这个消息指针给mb邮箱*/8rt_mb_send(mb,(rt_uint32_t)msg_ptr);

申请结构体大小的内存空间,返回的指针指向了结构体,当结构体中的信息处理完,那么可以将指向结构体的指针作为邮件发送到邮箱中,而在接收邮件的线程中完成对结构体信息的读取操作,在完成操作后应当释放内存,因为收取过来的是指针,而 msg_ptr 是一个新分配出来的内存块,所以在接收线程处理完毕后,需要释放相应的内存块:

1structmsg*msg_ptr;23if(rt_mb_recv(mb,(rt_uint32_t*)&msg_ptr)==RT_EOK)4{5/*在接收线程处理完毕后,需要释放相应的内存块*/6rt_free(msg_ptr);7}

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 操作系统
    +关注

    关注

    37

    文章

    6284

    浏览量

    121874
  • 邮箱
    +关注

    关注

    0

    文章

    5

    浏览量

    7769
  • RT-Thread
    +关注

    关注

    31

    文章

    1148

    浏览量

    38867

原文标题:社区新人的RT-Thread学习笔记6——邮箱

文章出处:【微信号:RTThread,微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    RT-thread内核之邮箱

    邮箱rt_err_t rt_mb_detach(rt_mailbox_t mb);使用该函数接口后,内核先唤醒所有挂在该邮箱上的线程(线程
    发表于 03-06 17:15

    RT-Thread 架构图

    RT-Thread 架构图:RT-Thread开发指南开源地址内核层:RT-Thread 内核,是 RT-Thread 的核心部分,包括了内
    发表于 12-16 08:15

    一文读懂RT-Thread操作系统的邮箱机制

    RT-Thread操作系统的邮箱有何作用?RT-Thread操作系统的邮箱机制管理数据结构是怎样
    发表于 02-28 08:10

    RT-Thread系统的信号如何进行管理和使用呢

    RT-Thread 中,仿照类似的原理,实现信号机制,用于线程(任务)间通信。本篇文章,我们来学习 RT-Thread 信号的相关内容,以及如何进行
    发表于 03-31 14:01

    怎样去操作RT-Thread系统的消息邮箱

    _EFULL 。接收邮件过程中,当邮箱中不存在邮件且超时时间不为 0 时,邮件收取过程将变成阻塞方式。 此时,只能由线程进行邮件的收取。2. 邮箱控制块RT-Thread
    发表于 03-31 14:29

    浅析RT-Thread中事件集的工作机制

    RT-Thread 中的事件集,也就是其他 RTOS 中的事件标志组。事件集也是线程(任务)间同步的一种机制。前面介绍的两种线程间同步的方式(信号量和互斥量)都是一对一;而事件集可以实现一对多、多
    发表于 04-11 15:31

    如何将邮箱、消息队列与信号用于RT-Thread线程间的通信呢

    1、RT-Thread操作系统的线程间通信RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处
    发表于 04-15 15:36

    简述Kconfig在RT-Thread系统上的工作机制

    1、Kconfig在RT-Thread中的工作机制C语言项目的裁剪配置本质上通过条件编译和宏的展开来实现的,RT-Thread借助Kconfig这套
    发表于 05-09 14:56

    有关RT-Thread操作系统中邮箱接口的详细描述

    邮箱是一种简单的线程间消息传递方式,在 RT-Thread 操作系统的实现中能够一次传递 4 字节邮件, 并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数 (邮件数由创建、初始化
    发表于 08-19 15:09

    rt-thread邮箱-队列区别是什么?

    今天研究了下rt-thread,里面的邮箱和队列,除了消息尺寸不同之外还有别的差别吗?怎么感觉像是同一种东西。
    发表于 10-31 08:29

    rt-thread 线程间同步和通信你用对了吗

    系统优化系列先停一停,总对人指指点点会让大家反感的。今天给各位 rt-thread 使用者一些使用信号量、邮箱、消息队列等同步和通信机制的...
    发表于 01-25 14:43 0次下载
    <b class='flag-5'>rt-thread</b> 线程间同步和通信你用对了吗

    比较一下Freertos和RT-Thread

    RT-Thread 内核,是 RT-Thread 的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理
    的头像 发表于 04-26 15:44 4732次阅读

    RT-Thread全球技术大会:萤石EZloT SDK对RT-Thread的支持以及多芯片平台管理策略

    RT-Thread全球技术大会:萤石EZloT SDK对RT-Thread的支持以及多芯片平台管理策略           审核编辑:彭静  
    的头像 发表于 05-27 11:55 1411次阅读
    <b class='flag-5'>RT-Thread</b>全球技术大会:萤石EZloT SDK对<b class='flag-5'>RT-Thread</b>的支持<b class='flag-5'>以及</b>多芯片平台<b class='flag-5'>管理</b>策略

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

    RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制               审核编辑:彭静
    的头像 发表于 05-27 14:49 1236次阅读
    <b class='flag-5'>RT-Thread</b>全球技术大会:Kconfig在<b class='flag-5'>RT-Thread</b>中的<b class='flag-5'>工作</b><b class='flag-5'>机制</b>

    RT-Thread自动初始化机制

      在分析之前首先查阅 RT-Thread 的官方文档 [RT-Thread 自动初始化机制](https://www.rt-thread.org/document/site
    的头像 发表于 06-17 08:52 1893次阅读
    <b class='flag-5'>RT-Thread</b>自动初始化<b class='flag-5'>机制</b>