RT-Thread线程通信
在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取,根据读取到的全局变量值执行相应的动作,达到通信协作的目的。RT-Thread 中则提供了更多的工具帮助在不同的线程中间传递信息,本章会详细介绍这些工具。学习完本章,大家将学会如何将邮箱、消息队列、信号用于线程间的通信。
一 邮箱
1.1 邮箱的工作机制
邮箱也称作交换消息,线程或中断服务例程把一封4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。邮箱中的每一封邮件只能容纳固定的4 字节内容(针对32 位处理系统,指针的大小即为4 个字节,所以一封邮件恰好能够容纳一个指针)。
非阻塞方式的邮件发送过程能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为0 时,邮件收取过程将变成阻塞方式。在这类情况下,只能由线程进行邮件的收取。
当一个线程向邮箱发送邮件时,如果邮箱没满,将把邮件复制到邮箱中。如果邮箱已经满了,发送线程可以设置超时时间,选择等待挂起或直接返回- RT_EFULL。如果发送线程选择挂起等待,那么当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送。
当一个线程从邮箱中接收邮件时,如果邮箱是空的,接收线程可以选择是否等待挂起直到收到新的邮件而唤醒,或可以设置超时时间。当达到设置的超时时间,邮箱依然未收到邮件时,这个选择超时等待的线程将被唤醒并返回- RT_ETIMEOUT。如果邮箱中存在邮件,那么接收线程将复制邮箱中的4 个字节邮件到接收缓存中。
1.2 邮箱的管理
1.2.1 邮箱控制块
在RT-Thread 中,邮箱控制块是操作系统用于管理邮箱的一个数据结构,由结构体struct rt_mailbox
表示。另外一种C 表达方rt_mailbox_t
,表示的是邮箱的句柄,在C 语言中的实现是邮箱控制块的指针。邮箱控制块结构的详细定义请见以下代码:
struct rt_mailbox
{
struct rt_ipc_object parent;
rt_uint32_t* msg_pool; /* 邮箱缓冲区的开始地址*/
rt_uint16_t size; /* 邮箱缓冲区的大小*/
rt_uint16_t entry; /* 邮箱中邮件的数目*/
rt_uint16_t in_offset, out_offset; /* 邮箱缓冲的进出指针*/
rt_list_t suspend_sender_thread; /* 发送线程的挂起等待队列*/
};
typedef struct rt_mailbox* rt_mailbox_t;
1.2.2 邮箱的管理
- 创建与删除
创建邮箱对象时会先从对象管理器中分配一个邮箱对象,然后给邮箱动态分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4 字节)与邮箱容量的乘积,接着初始化接收邮件数目和发送邮件在邮箱中的偏移量。动态创建一个邮箱对象可以调用如下的函数接口:
rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);
删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是- RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象。相应的,删除邮箱的函数接口如下:
rt_err_t rt_mb_delete (rt_mailbox_t mb);
- 初始化与剥离
初始化邮箱用于静态邮箱对象,内存是在系统编译时由编译器分配的,一般放于读写数据段或未初始化数据段中,其余的初始化工作与创