一、邮箱工作机制

RT_thread操作系统中的邮箱用于线程间通信,特点是开销较小,效率比较高。邮箱中的每一封邮件只能容纳固定的4字节内容(针对32位处理系统。指针的大小即为4字节,所以一封邮件恰好能容纳一个指针)

线程或中断服务例程把一封4字节长度的邮件发到邮箱中,而其他需要的线程可以通过邮箱中接收这些邮件并处理。

邮箱控制块:

RT-Thread中使用邮箱实现线程异步通信工作,具有如下特性:

邮件支持先进先出方式排队与优先级排队方式,支持异步读写工作方式。
发送与接收邮件均支持超时机制。
一个线程能够从任意一个消息队列接收和发送邮件。
多个线程能够向同一个邮箱发送邮件和从中接收邮件。
邮箱中的每一封邮件只能容纳固定的4字节内容(可以存放地址)。
当队列使用结束后,需要通过删除邮箱以释放内存。

邮箱与消息队列很相似,消息队列中消息的长度是可以由用户配置的,但邮箱中邮件的大小却只能是固定容纳4字节的内容,所以,使用邮箱的开销是很小的,因为传递的只能是4字节以内的内容,那么其效率会更高。

运作机制
创建邮箱对象时会先创建一个邮箱对象控制块,然后给邮箱分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4字节)与邮箱容量的乘积,接着初始化接收邮件和发送邮件在邮箱中的偏移量,接着再初始化消息队列,此时消息队列为空。

线程或者中断服务程序都可以给邮箱发送邮件,非阻塞方式的邮件发送过程能够安全的应用于中断服务中,中断服务函数、定时器向线程发送消息的有效手段,而阻塞方式的邮件发送只能应用于线程中。当发送邮件时,当且仅当邮箱还没满邮件的时候才能进行发送,如果邮箱已满,可以根据用户设定的等待时间进行等待,当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送的过程,当等待时间到了还未完成发送邮件,或者未设置等待时间,此时发送邮件失败,发送邮件的线程或者中断程序会收到一个错误码(-RT_EFULL)。线程发送邮件可以带阻塞,但在中断中不能采用任何带阻塞的方式发送邮件。

二、邮箱的操作

1、RT-Thread 创建一个邮箱有两种方式:动态创建、静态初始化。

动态创建一个邮箱的系统函数如下,调用这个函数创建一个邮箱时,内核会先从对象管理器中分配一个邮箱对象,然后创建一个邮箱控制块,接着对邮箱控制块进行初始化,包括邮箱缓冲区地址、邮件数目、发送邮件在邮箱中的偏移等。

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

(1)入口参数:

name:邮箱名称。
size:邮箱容量。
flag:邮箱标志,它可以取如下数值:RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO。

(2)返回值:

RT_NULL:创建失败。
邮箱对象的句柄:创建成功。

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

rt_err_t rt_mb_delete (rt_mailbox_t mb);

(1)入口参数:

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

(2)返回值:

RT_EOK:成功。

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

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

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),然后将该邮箱对象从内核对象管理器中脱离。脱离邮箱使用下面的接口:

rt_err_t rt_mb_detach(rt_mailbox_t mb);

(1)入口参数:

mb:邮箱对象的句柄。

(2)返回值:

RT_EOK:成功。

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

rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);

(1)入口参数:

mb:邮箱对象的句柄。
value:邮件内容。

(2)返回值:

RT_EOK:发送成功。
RT_EFULL:邮箱已经满了。

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

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

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

(1)入口参数:

mb:邮箱对象的句柄。
value:邮件内容。
timeout:超时时间。

(2)返回值:

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

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

rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);

(1)入口参数:

mb:邮箱对象的句柄。
value:邮件内容。
timeout:超时时间。

(2)返回值:

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

三、基于STM32的邮箱示例

采用RTT&正点原子联合出品潘多拉开发板,基于STM32。创建一个邮箱,两个线程,其中一个线程用于发送邮件,另外一个线程由于接收邮件。通过按下不同按键发送不同的邮件内容,根据读取到右邮件内容执行不同操作。当读取到内容为KEY0按下时点亮RGB红灯,其他熄灭,当读取到内容为KEY1按下时点亮RGB蓝灯,其他熄灭,当读取到内容为KEY0按下时点亮RGB绿灯,其他熄灭。

#include "rtthread.h"
#include "string.h"
#include "mailbox_app.h"
#include "led.h"
#include "key.h"/* 线程句柄 */
static rt_thread_t thread1 = RT_NULL;
static rt_thread_t thread2 = RT_NULL;/* 邮箱句柄 */
static rt_mailbox_t mailbox1 = RT_NULL;char mailbox_msg_key0_press[] = "mailbox_msg_key0_press";
char mailbox_msg_key1_press[] = "mailbox_msg_key1_press";
char mailbox_msg_key2_press[] = "mailbox_msg_key2_press";/**************************************************************
函数名称 : thread1_recv_mailbox_msg
函数功能 : 线程1入口函数,用于接收邮件
输入参数 : parameter:入口参数
返回值      : 无
备注       : 无
**************************************************************/
void thread1_recv_mailbox_msg(void *parameter)
{char *mb_msg;while(1){if(rt_mb_recv(mailbox1, (rt_uint32_t *)&mb_msg, RT_WAITING_FOREVER) == RT_EOK){rt_kprintf("recv mb_msg:%s\r\n", mb_msg);if(0 == strcmp(mb_msg, "mailbox_msg_key0_press")){LED_R(0);LED_B(1);LED_G(1);}else if(0 == strcmp(mb_msg, "mailbox_msg_key1_press")){LED_R(1);LED_B(0);LED_G(1);}else if(0 == strcmp(mb_msg, "mailbox_msg_key2_press")){LED_R(1);LED_B(1);LED_G(0);}}rt_thread_mdelay(1);}
}/**************************************************************
函数名称 : thread2_send_mailbox_msg
函数功能 : 线程2入口函数,用于发送邮件
输入参数 : parameter:入口参数
返回值      : 无
备注       : 无
**************************************************************/
void thread2_send_mailbox_msg(void *parameter)
{u8 key;while(1){key = key_scan(0);if(key== KEY0_PRES){rt_mb_send(mailbox1, (rt_uint32_t)&mailbox_msg_key0_press);}else if(key== KEY1_PRES){rt_mb_send(mailbox1, (rt_uint32_t)&mailbox_msg_key1_press);}else if(key== KEY2_PRES){rt_mb_send(mailbox1, (rt_uint32_t)&mailbox_msg_key2_press);}rt_thread_mdelay(1);}
}void rtthread_mailbox_test(void)
{mailbox1 = rt_mb_create("mailbox1", 12, RT_IPC_FLAG_FIFO);  /* FIFO模式 */if(mailbox1 != RT_NULL){rt_kprintf("RT-Thread create mailbox successful\r\n");}else{rt_kprintf("RT-Thread create mailbox failed\r\n");return;}thread1 = rt_thread_create("thread1",thread1_recv_mailbox_msg,NULL,512,3,20);if(thread1 != RT_NULL){rt_thread_startup(thread1);;}else{rt_kprintf("create thread1 failed\r\n");return;}thread2 = rt_thread_create("thread2",thread2_send_mailbox_msg,NULL,1024,2,20);if(thread2 != RT_NULL){rt_thread_startup(thread2);;}else{rt_kprintf("create thread2 failed\r\n");return;}
}

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

邮箱是一种简单的线程间消息传递方式,特点是开销比较低,效率较高。在 RT-Thread 操作系统的实现中能够一次传递一个 4 字节大小的邮件,并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数 (邮件数由创建、初始化邮箱时指定的容量决定)。邮箱中一封邮件的最大长度是 4 字节,所以邮箱能够用于不超过 4 字节的消息传递。
由于在 32 系统上 4 字节的内容恰好可以放置一个指针,因此当需要在线程间传递比较大的消息时,可以把指向一个缓冲区的指针作为邮件发送到邮箱中,即邮箱也可以传递指针,例如:

struct msg
{rt_uint8_t *data_ptr;rt_uint32_t data_size;
};

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

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

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

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

RT_thread邮箱的使用相关推荐

  1. RT_Thread_进程间通讯——邮箱

    1.邮箱 邮箱是线程间通信的一种方法,可以传递数据. 每封邮件4个字节大小(32 位CPU,指针的大小为 4 个字节,所以一封邮件恰好能够容纳一个指针). struct rt_mailbox {str ...

  2. rt_thread nano GD32F407移植

    rt_thread nano GD32F407移植 文章目录 rt_thread nano GD32F407移植 1.简介 2.RT-Thread Nano移植 移植目结构 移植步骤 添加文件 屏蔽中 ...

  3. RTT-thread 邮箱的管理方式

    邮箱的管理方式 邮箱控制块是一个结构体,其中含有事件相关的重要参数,在邮箱的功能实现中起重要的作用.邮箱的相关接口如下图所示,对一个邮箱的操作包含:创建 / 初始化邮箱.发送邮件.接收邮件.删除 / ...

  4. RT-Thread 内核学习 (十三)邮箱的使用

    邮箱工作机制 RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高.邮箱中的每一封邮件只能容纳固定的4字节内容(针对32位处理器系统,指针大小即为4个字节,所以一封邮件恰好能够 ...

  5. 基础篇_06_IPC之邮箱及消息队列

    邮箱和消息队列,都是在需要数据交换的场景下使用. 那么首先,先介绍邮箱.邮箱有一个具体的大小,邮箱内接收的数据的个数,不能超过邮箱总大小的四分之一. 1.邮箱的创建&消息队列的创建 /*静态邮 ...

  6. RT-Thread Studio入门(4)— 使用邮箱发送按键状态

    RT-Thread Studio使用邮箱发送按键状态 我们在上一个工程(RT-Thread Studio使用轮询法检测按键)的基础上,使用邮箱发送信息给另一个线程,然后对该信息进行处理 一.邮箱的介绍 ...

  7. Lumen / Laravel 使用网易邮箱 SMTP 发送邮件

    Laravel 是目前最流行的PHP框架,而Lumen 是 Laravel 的精简版,主要用于接口开发. Laravel 邮件发送服务基于 Symfony 组件 Swift Mailer. 本文记录了 ...

  8. Git 修改用户名和邮箱

    在修改用户名和邮箱之前可以查看下现在的用户名和邮箱 git config --list 或者使用 git config user.name git config user.email 修改用户名和邮箱 ...

  9. 在SharePoint 2010使用OWA查看自己邮箱的内容

    在SharePoint 2010使用OWA 背景: 利用 Outlook Web Access Web 部件,可以在 SharePoint 网站中显示从 Microsoft Exchange Serv ...

最新文章

  1. java date 加一天_Java 8中的时间JAVA成长之路
  2. 菜单消失_减肥的你,哪些食物应该从你的菜单消失?
  3. SSL与TLS的区别
  4. eclipse使用技巧---使用正则表达式查找替换
  5. Unicode和UTF-8之间的转换详解
  6. 直接选择排序算法汇总
  7. java hashMap缓存简单实现
  8. python字典返回键值对_从Python字典对象中提取键值对的子集?
  9. android的Imageview的src和background
  10. 000 初步使用Kotlin开发Android应用
  11. python基于Flask构建Web服务,解决Flask数据请求中的跨域问题
  12. 如何在数据库中查找和消除重复的数据?
  13. SVD——奇异值分解概述
  14. 利用Python制作属于自己的炫酷二维码
  15. TypesScript + Nodejs + Express + Mongoose 实现 RESTful API 实战视频教程(33 个视频)
  16. [转载]matlab中分号、冒号、逗号等常用标点符号的作用
  17. zdm各命令的功能和作用_ZDM命令 注释
  18. 图片不能置于底层怎么办_word图片为什么不能置于底层
  19. 如何用lisp画蔓叶线_用Autolisp 在AutoCAD中实现多种曲线的绘制
  20. 电子招投标给企业带来的实用价值

热门文章

  1. coreldraw x8里线段显示尺寸_CorelDRAW X8如何修改标尺单位
  2. 【JS(ES6,ES5类的创建及继承)】
  3. MySQL数据库的常用命令
  4. L1_SVD方法的稀疏矩阵的DOA估计
  5. 数据库索引分裂 问题分析
  6. 爱我所爱,行我所行,听从我心,无问西东
  7. 赵小楼《天道》《遥远的救世主》深度解析(94)去二不着一:自在自如逍遥境界
  8. Matlab 计算显色指数(Ra,R1-R15)、CCT、duv、Lux、XYZ三刺激值、CIE1931x、CIE1931y
  9. AUC值越大_AUC,ROC 讲解
  10. win7计算机虚拟内存,win7设置虚拟内存 win7虚拟内存如何设置