RT_Thread——邮箱
文章目录
- 一、邮箱的特性
- 二、邮箱操作函数
- 2.1 创建/初始化
- 2.2 删除/脱离
- 2.3 发邮件
- 2.4 收邮件
- 三、邮箱的基本使用
消息队列的本质是链表:
- 空闲消息块链表:往队列里写入消息时,先从空闲链表中得到消息块;从队列读出消息后,把消息块放入空闲链表
- 消息块头部链表:消息写入消息块后,该消息块被放到尾部;从队列里读消息时,从头部读。
使用消息队列可以传递各类大小的消息,它使用 memcpy 的方式写入消息、读出消息。如果我们只是传递很小的数据,比如一些数值,可以使用邮箱:它的效率更高。
本章涉及如下内容:
- 怎么创建、删除邮箱
- 邮箱中数据如何保存
- 怎么向邮箱写入数据、怎么从邮箱读取数据
- 在邮箱上阻塞是什么意思
一、邮箱的特性
邮箱的本质是环形缓冲区:
-
邮箱中的每一封邮件,只能容纳 4 字节内容(对于 32 位系统,指针大小刚好为 4 字节);
-
邮件的发送通常是非阻塞的,线程、中断都可以发送邮件;也可使用阻塞方式发送;
-
邮件的接收通常是阻塞的,取决于邮箱中是否有邮件;
-
当一个线程向邮箱发送邮件时:
- 如果邮箱没满,就把数值写入邮箱中
- 如果邮箱满了:
- 发送线程可以直接返回
-RT_EFULL
- 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序读了邮箱,会唤醒挂起的线程。
- 发送线程可以直接返回
-
当一个线程从邮箱接收邮件时:
- 如果邮箱不为空,就读取邮箱中的数值
- 如果邮箱为空:
- 接收线可以直接返回
-RT_ETIMOUT
- 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序写了邮箱,会唤醒挂起的线程。
- 接收线可以直接返回
二、邮箱操作函数
邮箱由邮箱控制块管理,由结构体 rt_mailbox
表示。
使用邮箱的流程:创建/初始化邮箱、发送邮件、接收邮件、删除/脱离邮箱。
2.1 创建/初始化
邮箱的创建有两种方法:
- 动态分配内存:
rt_mb_create()
,邮箱的内存在函数内部动态分配,分配的内存大小为邮件大小乘以邮箱容量 - 静态分配内存:
rt_mb_init()
,邮箱的内存事先分配好,比如可以是数组。
rt_mb_create()
函数原型如下:
rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);
rt_mb_init()
函数原型如下:
rt_err_t rt_mb_init(rt_mailbox_t mb, const char* name,void* msgpool, rt_size_t size, rt_uint8_t flag)
2.2 删除/脱离
不再使用一个邮箱时:
- 删除它:
rt_mb_delete()
,只能删除使用rt_mb_create()
创建的邮箱 - 脱离它:
rt_mb_detach()
,只能脱离使用rt_mb_init()
初始化的邮箱
删除邮箱的函数为 rt_mb_delete()
,它会释放内存。原型如下:
rt_err_t rt_mb_delete (rt_mailbox_t mb);
删除邮箱时,如果有线程在等待该邮箱,则内核先唤醒这些线程(线程返回值是 -RT_ERROR
),然后再释放邮箱使用的内存,最后删除邮箱对象。
脱离邮箱将使邮箱对象被从内核对象管理器中脱离。原型如下:
rt_err_t rt_mb_detach(rt_mailbox_t mb);
脱离消息邮箱时,如果有线程在等待该邮箱,则内核会先唤醒这些线程(线程返回值是 - RT_ERROR
)。
2.3 发邮件
RT-Thread 有三个发送邮件的函数:
- rt_mb_send() 发送邮件
- rt_mb_send_wait() 等待方式发送邮件
- rt_mb_urgent() 发送紧急邮件
线程或者中断服务程序都可以通过往邮箱里写入邮件。
发送的邮件,可以是 32 位的任意格式数据,可以是一个整型值或者一个指向某块内存的指针。
使用 rt_mq_send()
发送消息时,只有在邮箱有可用的空闲空间时,才能成功发送消息,否则返回错误码(-RT_EFULL
)。
使用 rt_mq_send_wait()
发送消息时,如果邮箱没有可用的空闲空间,会根据 timeout参数等待,超时后才返回错误。
使用 rt_mq_urgent()
发送消息时,只有在邮箱有可用的空闲空间,它才会把邮件插在邮件队首,以便这个邮件能被第一时间读取。
发送邮件的函数原型如下:
rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);
参数说明如下:
等待方式发送邮件的函数原型如下:
rt_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value,rt_int32_t timeout);
参数说明如下:
发送紧急邮件的函数原型如下:
rt_err_t rt_mb_urgent (rt_mailbox_t mb, rt_ubase_t value);
参数说明如下:
2.4 收邮件
当邮箱有邮件时,使用收邮件函数,可以从邮箱接收邮件。
如果没有邮件,根据指定的 timeout
参数等待,直到超时结束。
函数原型如下:
rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);
参数说明如下:
三、邮箱的基本使用
本节代码为:RT-Thread_10_queue_mailbox。
程序开头一些定义:
#include <string.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>#define THREAD_PRIORITY 15 //设置线程优先级
#define THREAD_STACK_SIZE 512 //设置线程栈大小
#define THREAD_TIMESLICE 15 //设置线程时间片大小static struct rt_thread *thread1; //定义线程1句柄指针
static struct rt_thread *thread2; //定义线程2句柄指针static struct rt_mailbox mb; //邮箱控制块
static char mb_pool[128]; //放置邮件的内存池
main 函数中创建了邮箱、创建了发送线程、接收线程:代码如下:
int main(void)
{rt_err_t result;/* 初始化邮箱 */result = rt_mb_init(&mb, //邮箱对象的句柄"mbt", //邮箱的名字&mb_pool[0], //内存池指向mb_poolsizeof(mb_pool) / 4, //邮箱中能容纳的邮件数量,每封邮件占四字节 RT_IPC_FLAG_FIFO); //如果有多个线程等待,按照先来先得到的方法分配邮件 if (result != RT_EOK){rt_kprintf("rt_mb_init ERR\n");return -1;}/* 创建动态线程thread1,优先级为 THREAD_PRIORIT-1 = 14 */thread1 = rt_thread_create("thread1", //线程名字thread1_entry, //入口函数RT_NULL, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY - 1, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程1 */if (thread1 != RT_NULL)rt_thread_startup(thread1); /* 创建动态线程thread2,优先级为 THREAD_PRIORIT = 15 */thread2 = rt_thread_create("thread2", //线程名字thread2_entry, //入口函数RT_NULL, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程2 */if (thread2 != RT_NULL)rt_thread_startup(thread2); return 0;
}
发送线程的函数中,不断往邮箱发送邮件,代码如下:
/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{rt_err_t result;int count = 0;/* 线程1 */while(1){/* 发送邮箱 */count++;result = rt_mb_send(&mb, (rt_ubase_t)count);if (result != RT_EOK){rt_kprintf("rt_mb_send ERR\n");}rt_kprintf("rt_mb_send:%d\n\r", count); rt_thread_mdelay(10);}
}
接收线程的函数中,读邮件、打印,代码如下:
/* 线程2的入口函数 */
static void thread2_entry(void *parameter)
{int val;/* 线程2 */while(1){/* 接收消息 */if (rt_mb_recv(&mb, (rt_ubase_t *)&val, RT_WAITING_FOREVER) == RT_EOK){rt_kprintf("rt_mb_recv:%d\n\r", val);}rt_thread_mdelay(5);}
}
运行结果如下图所示: