RTT-thread 邮箱的管理方式

邮箱的管理方式

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

创建和删除邮箱

动态创建一个邮箱对象可以调用如下的函数接口:

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

创建邮箱对象时会先从对象管理器中分配一个邮箱对象,然后给邮箱动态分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4 字节)与邮箱容量的乘积,接着初始化接收邮件数目和发送邮件在邮箱中的偏移量。下表描述了该函数的输入参数与返回值:

rt_mb_create() 的输入参数和返回值

参数

描述

name

邮箱名称

size

邮箱容量

flag

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

返回

——

RT_NULL

创建失败

邮箱对象的句柄

创建成功

当用 rt_mb_create() 创建的邮箱不再被使用时,应该删除它来释放相应的系统资源,一旦操作完成,邮箱将被永久性的删除。删除邮箱的函数接口如下:

rt_err_t rt_mb_delete (rt_mailbox_t mb);

删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是 - RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象。下表描述了该函数的输入参数与返回值:

rt_mb_delete() 的输入参数和返回值

参数

描述

mb

邮箱对象的句柄

返回

——

RT_EOK

成功

初始化和脱离邮箱

初始化邮箱跟创建邮箱类似,只是初始化邮箱用于静态邮箱对象的初始化。与创建邮箱不同的是,静态邮箱对象的内存是在系统编译时由编译器分配的,一般放于读写数据段或未初始化数据段中,其余的初始化工作与创建邮箱时相同。函数接口如下:

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

初始化邮箱时,该函数接口需要获得用户已经申请获得的邮箱对象控制块,缓冲区的指针,以及邮箱名称和邮箱容量(能够存储的邮件数)。下表描述了该函数的输入参数与返回值:

rt_mb_init() 的输入参数和返回值

参数

描述

mb

邮箱对象的句柄

name

邮箱名称

msgpool

缓冲区指针

size

邮箱容量

flag

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

返回

——

RT_EOK

成功

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

脱离邮箱将把静态初始化的邮箱对象从内核对象管理器中脱离。脱离邮箱使用下面的接口:

rt_err_t rt_mb_detach(rt_mailbox_t mb);

使用该函数接口后,内核先唤醒所有挂在该邮箱上的线程(线程获得返回值是 - RT_ERROR),然后将该邮箱对象从内核对象管理器中脱离。下表描述了该函数的输入参数与返回值:

rt_mb_detach() 的输入参数和返回值

参数

描述

mb

邮箱对象的句柄

返回

——

RT_EOK

成功

发送邮件

线程或者中断服务程序可以通过邮箱给其他线程发送邮件,发送邮件函数接口如下:

rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);

发送的邮件可以是 32 位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 -RT_EFULL 的返回值。下表描述了该函数的输入参数与返回值:

rt_mb_send() 的输入参数和返回值

参数

描述

mb

邮箱对象的句柄

value

邮件内容

返回

——

RT_EOK

发送成功

-RT_EFULL

邮箱已经满了

等待方式发送邮件

用户也可以通过如下的函数接口向指定邮箱发送邮件:

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_mb_send_wait() 的输入参数和返回值

参数

描述

mb

邮箱对象的句柄

value

邮件内容

timeout

超时时间

返回

——

RT_EOK

发送成功

-RT_ETIMEOUT

超时

-RT_ERROR

失败,返回错误

接收邮件

只有当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回 RT_EOK 的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。接收邮件函数接口如下:

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

接收邮件时,接收者需指定接收邮件的邮箱句柄,并指定接收到的邮件存放位置以及最多能够等待的超时时间。如果接收时设定了超时,当指定的时间内依然未收到邮件时,将返回 - RT_ETIMEOUT。下表描述了该函数的输入参数与返回值:

rt_mb_recv() 的输入参数和返回值

参数

描述

mb

邮箱对象的句柄

value

邮件内容

timeout

超时时间

返回

——

RT_EOK

发送成功

-RT_ETIMEOUT

超时

-RT_ERROR

失败,返回错误

邮箱使用示例

这是一个邮箱的应用例程,初始化 2 个静态线程,一个静态的邮箱对象,其中一个线程往邮箱中发送邮件,一个线程往邮箱中收取邮件。如下代码所示:

邮箱的使用例程

#include <rtthread.h> #define THREAD_PRIORITY 10 #define THREAD_TIMESLICE 5 /* 邮箱控制块 */ static struct rt_mailbox mb; /* 用于放邮件的内存池 */ static char mb_pool[128]; static char mb_str1[] = "I'm a mail!"; static char mb_str2[] = "this is another mail!"; static char mb_str3[] = "over"; ALIGN(RT_ALIGN_SIZE) static char thread1_stack[1024]; static struct rt_thread thread1; /* 线程 1 入口 */ static void thread1_entry(void *parameter) { char *str; while (1) { rt_kprintf("thread1: try to recv a mail\n"); /* 从邮箱中收取邮件 */ if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK) { rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str); if (str == mb_str3) break; /* 延时 100ms */ rt_thread_mdelay(100); } } /* 执行邮箱对象脱离 */ rt_mb_detach(&mb); } ALIGN(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; /* 线程 2 入口 */ static void thread2_entry(void *parameter) { rt_uint8_t count; count = 0; while (count < 10) { count ++; if (count & 0x1) { /* 发送 mb_str1 地址到邮箱中 */ rt_mb_send(&mb, (rt_uint32_t)&mb_str1); } else { /* 发送 mb_str2 地址到邮箱中 */ rt_mb_send(&mb, (rt_uint32_t)&mb_str2); } /* 延时 200ms */ rt_thread_mdelay(200); } /* 发送邮件告诉线程 1,线程 2 已经运行结束 */ rt_mb_send(&mb, (rt_uint32_t)&mb_str3); } int mailbox_sample(void) { rt_err_t result; /* 初始化一个 mailbox */ result = rt_mb_init(&mb, "mbt", /* 名称是 mbt */ &mb_pool[0], /* 邮箱用到的内存池是 mb_pool */ sizeof(mb_pool) / 4, /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */ RT_IPC_FLAG_FIFO); /* 采用 FIFO 方式进行线程等待 */ if (result != RT_EOK) { rt_kprintf("init mailbox failed.\n"); return -1; } rt_thread_init(&thread1, "thread1", thread1_entry, RT_NULL, &thread1_stack[0], sizeof(thread1_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread1); rt_thread_init(&thread2, "thread2", thread2_entry, RT_NULL, &thread2_stack[0], sizeof(thread2_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread2); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(mailbox_sample, mailbox sample);

仿真运行结果如下:

\ | / - RT - Thread Operating System / | \ 3.1.0 build Aug 27 2018 2006 - 2018 Copyright by rt-thread team msh >mailbox_sample thread1: try to recv a mail thread1: get a mail from mailbox, the content:I'm a mail! msh >thread1: try to recv a mail thread1: get a mail from mailbox, the content:this is another mail! … thread1: try to recv a mail thread1: get a mail from mailbox, the content:this is another mail! thread1: try to recv a mail thread1: get a mail from mailbox, the content:over

例程演示了邮箱的使用方法。线程 2 发送邮件,共发送 11 次;线程 1 接收邮件,共接收到 11 封邮件,将邮件内容打印出来,并判断结束。

邮箱的使用场合

邮箱是一种简单的线程间消息传递方式,特点是开销比较低,效率较高。在 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); }

猜你喜欢

转载自blog.csdn.net/qq_36958104/article/details/113108282
今日推荐