一、邮箱控制块:在include/rtdef.h中

#ifdef RT_USING_MAILBOX
/**
* mailbox structure
*/
struct rt_mailbox
{
struct rt_ipc_object parent; /**< inherit from ipc_object */ //继承自IPC对象 rt_uint32_t *msg_pool; /**< start address of message buffer */ //消息缓冲地址 rt_uint16_t size; /**< size of message pool */ //可存放的消息最大条数 rt_uint16_t entry; /**< index of messages in msg_pool */ //当前邮箱中存放的消息条数
rt_uint16_t in_offset; /**< input offset of the message buffer */ //消息存入的偏移位置
rt_uint16_t out_offset; /**< output offset of the message buffer */ //消息取出时的偏移位置 rt_list_t suspend_sender_thread; /**< sender thread suspended on this mailbox *///邮箱发送线程挂起链表(当没有取走时,发送线程会被挂起)
};
typedef struct rt_mailbox *rt_mailbox_t;
#endif

二、邮箱相关接口:在src/ipc.c中

创建邮箱:
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag);
创建邮箱对象时会先创建一个邮箱对象控制块,然后给邮箱分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4字节)与邮箱容量的乘积,接着初始化接收邮件和发送邮件在邮箱中的偏移量。 删除邮箱:
rt_err_t rt_mb_delete(rt_mailbox_t mb);
删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程获得返回值是-RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象。
初始化邮箱:
rt_err_t rt_mb_init(rt_mailbox_t mb, //邮箱对象的句柄
const char *name, //邮箱名称
void *msgpool, //缓冲区指针
rt_size_t size, //邮箱容量
rt_uint8_t flag); //邮箱标志(FIFO/PRIO)
与创建邮箱不同的是,此处静态邮箱对象所使用的内存空间是由用户线程指定的一个缓冲区空间,用户把缓冲区的指针传递给邮箱对象控制块,其余的初始化工作与创建邮箱时相同。
注: 这里的size参数指定的是邮箱的容量,即如果msgpool的字节数是N,那么邮箱容量应该是N/4 脱离邮箱:
rt_err_t rt_mb_detach(rt_mailbox_t mb);
使用该函数接口后,内核先唤醒所有挂在该邮箱上的线程(线程获得返回值是-RT_ERROR ),然后将该邮箱对象从内核对象管理器中删除。
等待方式发送邮件:
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参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这是发送线程将被唤醒返回错误码。 发送邮件:
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value);
此函数与rt_mb_send_wait(mb, value, )等价。发送的邮件可以是32位任意格式的数据,一个整型值或者一个指向缓冲区的指针。
当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到-RT_EFULL 的返回值。发送函数在邮箱满的时候会挂起当前所处的发送线程。 接收邮件:
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout);
只有当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回RT_EOK的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。接收邮件时,接收者需指定接收邮件的邮箱句柄,并指定接收到的邮件存放位置以及最多能够等待的超时时间。如果接收时设定了超时,当指定的时间内依然未收到邮件时,将返回-RT_ETIMEOUT。函数的整个流程为:
(1)接收邮件函数会判断当前邮箱是否为空且时间参数为0, 如果是则直接返回,然后进行一个while循环,在这个while循环中的判断条件是邮箱为空,为什么要这么做呢?因此程序将在这个 while循环内一直等待邮件的到达,只有邮件到达才会跳出这个循环(如果等待超时则直接函数返回了)。
(2)进入while循环后,程序再次判断当前的等待时间参数是否为0,如果是则直接返回。接着挂起当前线程,再次判断时间参数是否大于0,这里主要是扫除timeout=RT_WAITING_FOREVER的情况,因为RT_WAITING_FOREVER宏定义为-1.在if语句内记录当前的时间点,然后设置一定时器并开启,接着重新调度。在重新调度后,系统可能切换到其它线程,假设一段时间内,系统再次切换回来,原因可能有多种,1:邮箱被脱离,此时当前线程thread->error=-RT_ERROR;2 定时器时间到达,但是邮件还未到达,此时thread->error=-RT_ETIMEOUT;3:邮件到达,本线程在发送邮件函数中被唤醒(注:发送邮件函数中只是唤醒第一条等待邮件的线程),此时,thread->error还是保持原值RT_EOK不变;4:其它原因假设一段时间后线程切换回来,此时error的值也一直保持原样RT_EOK不变.因此,在重新调度了线程之后,才会有一个if语句通过判断thread->error的值是否为RT_EOK来判断当前线程是否被发送邮件函数唤醒。如果不是,则直接返回错误。
(3)接下来,按原则上说,当前线程一定是被发送邮件函数唤醒,因此,当前一定会存在接收的邮件,但是接下来的几行代码却是再次判断时间参数大于0的情况下,计算还剩余多多时间,然后返回到while循环接着循环。其原因为判断邮箱中是否真正存在邮件的唯一标准是while循环的判断条件,即邮箱内的接收信件条数不能为空,或为空,则判断循环,或不为空,则自然不会进行到while循环中了。但如果这时发现原来邮箱还是为空,那么当前线程则应该继续等待了,此时就应该计算出下一次循环中需要等待的剩下时间,好让下一循环进行精确等待这段时间。
(4)接下来就是取出接收到的邮件,并更新邮箱的进出口偏移位置及邮箱中的邮件数减1,这样操作过后,不要忘记邮箱内可能还保留着因之前邮箱空间不中而挂起的发送线程,这个时候由于读取邮件操作,那么肯定是至少有一个空出的位置,那么是时候唤醒可能挂起的发送线程了(如果存在的话)。最后重新调度一下。
控制邮箱:
rt_err_t rt_mb_control(rt_mailbox_t mb, rt_uint8_t cmd, void *arg);
只支持RT_IPC_CMD_RESET这一命令,表示复位邮箱(重新初始化邮箱)。

 三、小结

邮箱相关源码主要是在发送与接收上。发送时,由于当前邮箱可能空间已满,放不下要发送的邮件,此时,不得不挂起当前发送线程(如果存在时间参数的话),只要在接收函数读取出一条邮件时才会唤醒它。同理,如果当前邮箱为空,则接收函数会挂起当前的接收线程,直到有新的邮件到达(在发送函数中唤醒接收线程)或等待超时。

另外需要注意地是,rt-thread操作系统支持多个发送线程和多个接收线程,多个发送线程倒还好,倒是多个接收线程就不太好控制了,一般这种情况也不会用的,如果真的需要这种情况,那么多个接收线程就得好好控制了,因为,到底是哪个接收线程接收到邮件还不好说。

RT-thread内核之邮箱的更多相关文章

  1. RT Thread 通过ENV来配置SFUD,操作SPI Flash

    本实验基于正点原子stm32f4探索者板子 请移步我的RT Thread论坛帖子. https://www.rt-thread.org/qa/forum.php?mod=viewthread& ...

  2. STM32 + RT Thread OS 学习笔记[二]

    串口通讯例程 通过上面的练习,对STM32项目开发有了一个直观印象,接下来尝试对串口RS232进行操作. 1.   目标需求: 开机打开串口1,侦听上位机(使用电脑串口测试软件)发送的信息,然后原样输 ...

  3. STM32 + RT Thread OS 串口通讯

    1.   创建项目 a)   禁用Finsh和console b)   默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1.因 ...

  4. RT thread 设备驱动组件之USART设备

    本文以stm32f4xx平台介绍串口驱动,主要目的是:1.RTT中如何编写中断处理程序:2.如何编写RTT设备驱动接口代码:3.了解串行设备的常见处理机制.所涉及的主要源码文件有:驱动框架文件(usa ...

  5. STM32 + RT Thread OS 学习笔记[三]

    RTGUI 据说RTGUI是多线程的,因此与RT-Thread OS的耦合度较高,有可能要访问RT-Thread的线程控制块.如果要移植到其它OS,估计难度较大.目前还处于Alpha状态,最终将会包含 ...

  6. STM32 + RT Thread OS 学习笔记[四]

    1.  补注 a)      硬件,打通通讯通道 若学习者购买了学习板,通常可以在学习板提供的示例代码中找到LCD的相关驱动代码,基本上,这里的驱动的所有代码都可以从里面找到. 从上面的示意图可见,M ...

  7. RT Thread的SPI设备驱动框架的使用以及内部机制分析

    注释:这是19年初的博客,写得很一般,理解不到位也不全面.19年末得空时又重新看了RTThread的SPI和GPIO,这次理解得比较深刻.有时间时再整理上传. -------------------- ...

  8. RT Thread SPI设备 使用

    后记: 之前,我把SPI的片选在Cubemx中配置成了SPI_NSS.现在我给它改为了GPIO_OUTPUT.  同时参考了别人的类似的一个操作无线模块(采用SPI设备驱动)的例子程序(清楚了RTT的 ...

  9. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

随机推荐

  1. fastDFS 分布式文件系统应用

    环境准备 使用的系统软件 名称 说明 centos 7.x libfatscommon FastDFS分离出的一些公用函数包 FastDFS FastDFS本体 fastdfs-nginx-modul ...

  2. 使用Python访问HDFS

    最近接触到大数据,对于Skpark和Hadoop的料及都停留在第一次听到这个名词时去搜一把看看大概介绍免得跟不上时代的层次. 在实际读了点别人的代码,又自己写了一些之后,虽然谈不上理解加深,至少对于大 ...

  3. selenium自动化之显式等待和EC(expected_conditions)模块

    很多人都有这种经历,selenium脚本当前运行没问题,过了一段时间再运行就报错了,然后过几天又好了.其中的原因估计60%的人都知道,是因为元素加载这块有问题.通常的解决方案就是加上sleep或者隐式 ...

  4. Objective-C 内存管理和ARC

    内存管理 范围: 任何继承了NSObject的对象 对基本数据类型无效 原理: 每个对象内部都保存了一个与之相关联的整数 称为引用计数器 1.计数器的基本操作 当使用alloc new或者copy创建 ...

  5. 一步一步图文介绍SpriteKit使用TexturePacker导出的纹理集Altas

    1.为什么要使用纹理集? 游戏是一种很耗费资源的应用,特别是在移动设备中的游戏,性能优化是非常重要的 纹理集是将多张小图合成一张大图,使用纹理集有以下优点: 1.减少内存占用,减少磁盘占用: 2.减少 ...

  6. 微信小程序入门学习之事件 事件对象 冒泡非冒泡事件(1)

    这关于事件的学习,可以自己复制到微信开发者工具上自己运行试试. 首先这里有两个文件.js 和.wxml 文件 首先给出.js文件下代码 // pages/news/news.js Page({ /** ...

  7. [python]np.loadtxt报错

    np.loadtxt报错 通过pandas生成的cvs数据利用nump.loadtxt读取的时候 tmp = np.loadtxt('test.csv', dtype=np.str, delimite ...

  8. 中文乱码的处理—@北河的ppt

  9. 【springmvc+mybatis项目实战】杰信商贸-5.生产厂家DAO+SERVICE+CONTROLLER+JSP+配置文件

    上一篇我们创建了工程和一个Factory的po对象(javaBean),我们也写好了Mapper的映射文件,接下来我们来完成生产厂家的DAO与SERVICE,以及CONTROLLER,还有做显示的JS ...

  10. 小程序解析html和富文本编辑内容【亲测有效】

    首先去 https://github.com/icindy/wxParse 下载wxParse,只拷贝wxParse文件夹即可. 1.引入wxss @import "../../util/w ...