邮箱
简介
在多任务操作系统中,任务间通信是必须的,例如任务1负责检测系统的状态,任务2负责根据不同的状态作出不同的操作,状态值就需要从任务1传递到任务2,而邮箱就是一种任务间通信机制。邮箱每次可以传递固定字节大小的邮件,邮箱有缓冲区(邮箱资源池),可以缓存一定数量的邮件。邮箱没满的情况下,可以一直往邮箱里面发送邮件,邮箱满了可以选择超时等待;邮箱有邮件的情况下,可以从邮箱里面接收邮件,如果没有邮件,可以选择超时等待。在本操作系统中,一封邮件的大小固定为4byte,所有可以直接发送32位数据。在32位系统上,一个指针大小是4byte,所以可以用邮箱来发送指针,而收发双方都按照相同的方式去解析指针指向的内容,就可以达到传送各种类型数据的功能。
邮箱实现原理
邮箱有两个任务阻塞队列,因为邮箱发送和接收都有可能导致阻塞。当邮箱内没有邮件时,就会导致接收邮件任务阻塞,任务被放到阻塞队列,等待另一个任务发送邮件,阻塞任务被唤醒,并放到就绪队列;当邮箱内邮件满了,就会导致发送邮件任务阻塞,后续的处理过程和接收邮件类似。下图描述了任务接收邮件被阻塞,然后等待另一个任务发送邮件的处理过程。
图中(1),任务1先运行
图中(2),任务1获取邮件,由于此时没有邮件,获取失败
图中(3),任务1被放到阻塞队列
图中(4),任务2运行
图中(5),任务2发送邮件
图中(6),任务2发送的邮件,通过写索引放到了邮箱资源池
图中(7),任务1被放到就绪队列
图中(8),任务1运行
图中(9),任务1通过读索引取走邮件
邮箱实现中断和任务的通信
邮箱也可以用于中断和任务间的通信。例如,任务接收邮件,而中断程序发送邮件,但是需要注意的是中断中只能用非阻塞式的方式,即timeout参数需要设置为0,因为不为0的话,在邮件满了的情况下,就会导致阻塞,中断上下文不允许阻塞方式的,具体过程和信号量类似,此处不再详述。
重要定义及数据结构
邮箱宏定义
宏定义了阻塞任务的唤醒顺序。
#define OS_MB_WAKE_TYPE_PRIO 0x55
#define OS_MB_WAKE_TYPE_FIFO 0xAA
邮箱宏 | 说明 |
---|---|
OS_MB_WAKE_TYPE_PRIO | 按优先级唤醒 |
OS_MB_WAKE_TYPE_FIFO | 按FIFO唤醒 |
邮箱控制块结构体
struct os_mb
{
void *mail_pool; /* The address that doesn't do alignment for mail pool */
void *mail_pool_align; /* Aligned address of mail pool. */
os_list_node_t send_task_list_head; /* Sender tasks blocked on this mailbox. */
os_list_node_t recv_task_list_head; /* Receiver tasks blocked on this mailbox. */
os_list_node_t resource_node; /* Node in resource list */
os_uint16_t capacity; /* Max number of mails for this mailbox. */
os_uint16_t entry_count; /* Numbers of mails into mailbox. */
os_uint16_t read_index; /* Read index of mail pool. */
os_uint16_t write_index; /* Write index of mail pool. */
os_uint8_t object_inited; /* Indicates whether object is inited or deinited, value is
OS_KOBJ_INITED or OS_KOBJ_DEINITED */
os_uint8_t object_alloc_type; /* Indicates whether object is allocated dynamically or statically,
value is OS_KOBJ_ALLOC_TYPE_STATIC or OS_KOBJ_ALLOC_TYPE_DYNAMIC */
os_uint8_t wake_type; /* The type to wake up blocking tasks, value is OS_MB_WAKE_TYPE_PRIO
or OS_MB_WAKE_TYPE_FIFO */
char name[OS_NAME_MAX + 1]; /* Mailbox name. */
};
邮箱控制块成员变量 | 说明 |
---|---|
mail_pool | 邮箱资源池指针,指向邮箱资源池起始地址 |
mail_pool_alig | 邮箱资源池指针,指向邮箱资源池对齐后的起始地址 |
send_task_list_head | 邮件发送任务阻塞队列头,发送邮件时邮箱没有空闲消息块时将发送任务阻塞在该队列上 |
recv_task_list_head | 邮件接收任务阻塞队列头,接收邮件时邮箱没有邮件时将接收任务阻塞在该队列上 |
resource_node | 资源管理节点,通过该节点将创建的邮箱挂载到gs_os_mb_resource_list_head上 |
capacity | 邮件支持的最大邮件数量 |
entry_count | 邮箱中邮件数量 |
read_index | 邮件读索引,指示下次读取邮件时读取位置 |
write_index | 邮件写索引,指示下次发送邮件时写入位置 |
object_inited | 初始化状态,0x55表示已经初始化,0xAA表示已经去初始化,其他值为未初始化 |
object_alloc_type | 邮箱类型,0为静态邮箱,1为动态邮箱 |
wakt_type | 阻塞任务唤醒方式,0x55表示按优先级唤醒,0xAA表示按FIFO唤醒。可以通过属性设置接口进行设置 |
name | 邮箱名字,名字长度不能大于OS_NAME_MAX |
API列表
接口 | 说明 |
---|---|
os_mb_init | 以静态的方式初始化邮箱,邮箱对象的内存空间和邮箱缓冲区都由使用者提供 |
os_mb_deinit | 去初始化邮箱,与os_mb_init()匹配使用 |
os_mb_create | 以动态的方式创建并初始化邮箱,其邮箱对象的内存空间和邮箱缓冲区都是通过动态申请内存获得 |
os_mb_destroy | 销毁邮箱,并释放邮箱对象的内存空间和邮箱缓冲区的空间,与os_mb_create()匹配使用 |
os_mb_send | 发送邮件,若邮箱已满且设定了等待时间,则当前发送任务阻塞 |
os_mb_recv | 接收邮件,若邮箱为空且设定了等待时间,则当前接收任务阻塞 |
os_mb_set_wake_type | 对于阻塞在邮箱下的任务,设置唤醒阻塞任务的类型 |
os_mb_reset | 复位邮箱,使邮箱达到初始状态 |
os_mb_is_empty | 查询邮箱是否为空 |
os_mb_is_full | 查询邮箱是否为满 |
os_mb_get_capacity | 获取邮箱容量 |
os_mb_get_used_entry_count | 获取邮箱中邮件数量 |
os_mb_get_unused_entry_count | 获取邮箱中空闲资源数量 |
os_mb_init
该函数以静态的方式初始化邮箱,邮箱对象的内存空间和邮箱缓冲区都由使用者提供,函数原型如下:
os_err_t os_mb_init(os_mb_t *mb, const char *name, void *mail_pool, os_size_t mail_pool_size);
参数 | 说明 |
---|---|
mb | 邮箱控制块,由用户提供,并指向对应的邮箱控制块内存地址 |
name | 邮箱名字,其最大长度由OS_NAME_MAX 宏指定,多余部分会被自动截掉 |
mail_pool | 邮箱缓冲区的起始地址 |
mail_pool_size | 邮邮箱缓冲区的大小,以byte为单位 |
返回 | 说明 |
OS_EOK | 初始化消息队列成功 |
OS_EINVAL | 无效参数 |
os_mb_deinit
该函数用于去初始化邮箱,与os_mb_init()匹配使用,函数原型如下:
os_err_t os_mb_deinit(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
OS_EOK | 去初始化邮箱成功 |
os_mb_create
该函数以动态的方式创建并初始化邮箱,其邮箱对象的内存空间和邮箱缓冲区都是通过动态申请内存获得,函数原型如下:
os_mb_t *os_mb_create(const char *name, os_size_t max_mails);
参数 | 说明 |
---|---|
name | 邮箱名字,其最大长度由OS_NAME_MAX 宏指定,多余部分会被自动截掉 |
max_mails | 此邮箱支持的最大邮件个数 |
返回 | 说明 |
非OS_NULL | 消息队列创建成功 |
OS_NULL | 消息队列创建失败 |
os_mb_destroy
该函数用于销毁邮箱,并释放邮箱对象的内存空间和邮箱缓冲区的空间,函数原型如下:
os_err_t os_mb_destroy(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
OS_EOK | 销毁邮箱成功 |
os_mb_send
该函数用于发送邮件,若邮箱已满且设定了等待时间,则当前发送任务阻塞,函数原型如下:
os_err_t os_mb_send(os_mb_t *mb, os_ubase_t value, os_tick_t timeout);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
value | 邮件内容 |
timeout | 邮件暂时不能发送的等待超时时间。若为OS_NO_WAIT,则等待时间为0;若为OS_WAIT_FOREVER,则永久等待直到邮件发送;若为其它值,则等待timeout时间或者邮件发送为止,并且其他值时timeout必须小于OS_TICK_MAX / 2 |
返回 | 说明 |
OS_EOK | 邮件发送成功 |
OS_EFULL | 不等待且邮件未发送 |
OS_ETIMEOUT | 等待超时且邮件未发送 |
OS_ERROR | 其他错误 |
os_mb_recv
该函数用于接收邮件,若邮箱为空且设定了等待时间,则当前接收任务阻塞,函数原型如下:
os_err_t os_mb_recv(os_mb_t *mb, os_ubase_t *value, os_tick_t timeout);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
value | 接收的邮件内容 |
timeout | 消息暂时接收不到时的等待超时时间。若为OS_NO_WAIT,则等待时间为0;若为OS_WAIT_FOREVER,则永久等待直到接收到消息;若为其它值,则等待timeout时间或者接收到消息为止,并且其他值时timeout必须小于OS_TICK_MAX / 2 |
返回 | 说明 |
OS_EOK | 邮件接收成功 |
OS_EEMPTY | 不等待且未接收到邮件 |
OS_ETIMEOUT | 等待超时未接收到邮件 |
OS_ERROR | 其他错误 |
os_mb_set_wake_type
该函数用于,对于阻塞在邮箱下的任务,设置唤醒阻塞任务的类型,函数原型如下:
os_err_t os_mb_set_wake_type(os_mb_t *mb, os_uint8_t wake_type);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
wake_type | OS_MB_WAKE_TYPE_PRIO 设置唤醒阻塞任务的类型为按优先级唤醒(邮箱创建后默认为使用此方式),OS_MB_WAKE_TYPE_FIFO 设置唤醒阻塞任务的类型为先进先出唤醒 |
返回 | 说明 |
OS_EOK | 设置唤醒阻塞任务类型成功 |
OS_EBUSY | 设置唤醒阻塞任务类型失败 |
os_mb_reset
复位邮箱,使邮箱达到初始状态,函数原型如下:
void os_mb_reset(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
无 | 无 |
os_mb_is_empty
该函数用于查询邮箱是否为空,函数原型如下:
os_bool_t os_mb_is_empty(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
OS_TRUE | 邮箱为空 |
OS_FALSE | 邮箱不为空 |
os_mb_is_full
该函数用于查询邮箱是否为满,函数原型如下:
os_bool_t os_mb_is_full(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
OS_TRUE | 邮箱为满 |
OS_FALSE | 邮箱不为满 |
os_mb_get_capacity
该函数用于获取邮箱容量,函数原型如下:
os_uint16_t os_mb_get_capacity(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
os_uint16_t | 邮箱容量 |
os_mb_get_used_entry_count
该函数用于获取邮箱中邮件数量,函数原型如下:
os_uint16_t os_mb_get_used_entry_count(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
os_uint16_t | 邮箱中的邮件数量 |
os_mb_get_unused_entry_count
该函数用于获取邮箱中空闲资源数量,函数原型如下:
os_uint16_t os_mb_get_unused_entry_count(os_mb_t *mb);
参数 | 说明 |
---|---|
mb | 邮箱控制块 |
返回 | 说明 |
os_uint16_t | 邮箱中空闲资源数量 |
配置选项
OneOS在使用邮箱时提供了功能裁剪的配置,具体配置如下图所示:
配置项 | 说明 |
---|---|
Enable mailboxg | 使能邮箱功能,如果不使能该功能,邮箱相关的源代码就不会编译,默认使能 |
使用示例
静态邮箱使用示例
本例采用静态方式初始化了一个邮箱。然后创建了任务1,每隔一段时间发送一封邮件(32位整型数据),当邮箱满了之后,发送任务阻塞;之后创建任务2接收邮件,当读取邮件后,发送任务被唤醒可以继续发送邮件;当邮件被全部接收完成后,再次接收邮件时,接收任务会阻塞;后续发送任务每发送一封邮件,接收任务就被唤醒一次。
#include <oneos_config.h>
#include <dlog.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <os_mb.h>
#define TEST_TAG "TEST"
#define TASK_STACK_SIZE 1024
#define TASK1_PRIORITY 15
#define TASK2_PRIORITY 16
#define MB_MAX_MAILS 10
#define MB_POLL_SIZE (MB_MAX_MAILS * sizeof(os_uint32_t))
static char mb_pool[MB_POLL_SIZE];
static os_mb_t mb_static;
void task1_entry(void *para)
{
os_uint32_t send_data = 0;
while (1)
{
send_data++;
LOG_W(TEST_TAG, "task1 send_data:%d", send_data);
if(OS_EOK == os_mb_send(&mb_static, send_data, OS_WAIT_FOREVER))
{
LOG_W(TEST_TAG, "task1 send OK");
}
else
{
LOG_W(TEST_TAG, "task1 send err");
}
os_task_msleep(100);
}
}
void task2_entry(void *para)
{
os_uint32_t recv_data = 0;
while (1)
{
if (OS_EOK == os_mb_recv(&mb_static, &recv_data, OS_WAIT_FOREVER))
{
LOG_W(TEST_TAG, "task2 recv_data:%d", recv_data);
}
}
}
void mailbox_static_sample(void)
{
os_task_t *task1 = OS_NULL;
os_task_t *task2 = OS_NULL;
os_mb_init(&mb_static, "mailbox_static", &mb_pool[0], MB_POLL_SIZE);
task1 = os_task_create("task1",
task1_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK1_PRIORITY);
if (task1)
{
LOG_W(TEST_TAG, "mailbox_static_sample startup task1");
os_task_startup(task1);
}
os_task_msleep(1200);
task2 = os_task_create("task2",
task2_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK2_PRIORITY);
if (task2)
{
LOG_W(TEST_TAG, "mailbox_static_sample startup task2");
os_task_startup(task2);
}
}
SH_CMD_EXPORT(static_mailbox, mailbox_static_sample, "test staitc mailbox");
运行结果如下:
sh>static_mailbox
W/TEST: mailbox_static_sample startup task1
W/TEST: task1 send_data:1
W/TEST: task1 send OK
W/TEST: task1 send_data:2
W/TEST: task1 send OK
W/TEST: task1 send_data:3
W/TEST: task1 send OK
W/TEST: task1 send_data:4
W/TEST: task1 send OK
W/TEST: task1 send_data:5
W/TEST: task1 send OK
W/TEST: task1 send_data:6
W/TEST: task1 send OK
W/TEST: task1 send_data:7
W/TEST: task1 send OK
W/TEST: task1 send_data:8
W/TEST: task1 send OK
W/TEST: task1 send_data:9
W/TEST: task1 send OK
W/TEST: task1 send_data:10
W/TEST: task1 send OK
W/TEST: task1 send_data:11
W/TEST: mailbox_static_sample startup task2
W/TEST: task1 send OK
W/TEST: task2 recv_data:1
W/TEST: task2 recv_data:2
W/TEST: task2 recv_data:3
W/TEST: task2 recv_data:4
W/TEST: task2 recv_data:5
W/TEST: task2 recv_data:6
W/TEST: task2 recv_data:7
W/TEST: task2 recv_data:8
W/TEST: task2 recv_data:9
W/TEST: task2 recv_data:10
W/TEST: task2 recv_data:11
sh>W/TEST: task1 send_data:12
W/TEST: task1 send OK
W/TEST: task2 recv_data:12
W/TEST: task1 send_data:13
W/TEST: task1 send OK
W/TEST: task2 recv_data:13
W/TEST: task1 send_data:14
W/TEST: task1 send OK
W/TEST: task2 recv_data:14
W/TEST: task1 send_data:15
W/TEST: task1 send OK
动态邮箱使用示例
本例采用动态的方式创建并初始化了一个邮箱;本例所展示的发送邮件的方式更为通用和更加灵活。在任务1中申请了一块空间,填充好要发送的数据后,将数据区域的指针作为邮件内容发送出去;在任务2中,接收到邮件内容后,将指针指向的数据按照相同的方式解析,之后释放申请的空间。
#include <oneos_config.h>
#include <dlog.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <string.h>
#include <os_memory.h>
#include <os_mb.h>
#define TEST_TAG "TEST"
#define TASK_STACK_SIZE 1024
#define TASK1_PRIORITY 15
#define TASK2_PRIORITY 16
#define MB_MAX_MAILS 10
#define TEST_NAME_MAX 16
#define STUDENT_NUM 5
static os_mb_t *mb_dynamic;
struct student_score
{
char name[TEST_NAME_MAX];
os_uint32_t score;
};
void task1_entry(void *para)
{
os_uint32_t i = 0;
struct student_score *student_data = OS_NULL;
char *name[STUDENT_NUM] = {"xiaoming", "xiaohua", "xiaoqiang", "xiaoli", "xiaofang"};
os_uint32_t score[STUDENT_NUM] = {80, 85, 90, 95, 96};
for (i = 0; i < STUDENT_NUM; i++)
{
student_data = os_malloc(sizeof(struct student_score));
LOG_W(TEST_TAG, "task1 malloc ptr:%p", student_data);
if (student_data)
{
memset(student_data->name, 0, TEST_NAME_MAX);
strncpy(student_data->name, name[i], TEST_NAME_MAX);
student_data->score = score[i];
if(OS_EOK == os_mb_send(mb_dynamic, (os_uint32_t)student_data, OS_WAIT_FOREVER))
{
LOG_W(TEST_TAG, "task1 send -- name:%s score:%d", student_data->name, student_data->score);
}
}
os_task_msleep(100);
}
}
void task2_entry(void *para)
{
os_uint32_t recv_data = 0;
while (1)
{
if (OS_EOK == os_mb_recv(mb_dynamic, &recv_data, OS_WAIT_FOREVER))
{
LOG_W(TEST_TAG, "task2 recv -- name:%s score:%d", ((struct student_score *)recv_data)->name, ((struct student_score *)recv_data)->score);
LOG_W(TEST_TAG, "task2 free ptr :%p", (void*)recv_data);
os_free((void*)recv_data);
}
}
}
void mailbox_dynamic_sample(void)
{
os_task_t *task1 = OS_NULL;
os_task_t *task2 = OS_NULL;
mb_dynamic = os_mb_create("mailbox_dynamic", MB_MAX_MAILS);
if(!mb_dynamic)
{
LOG_W(TEST_TAG, "mailbox_dynamic_sample mailbox create ERR");
return;
}
task1 = os_task_create("task1",
task1_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK1_PRIORITY);
if (task1)
{
LOG_W(TEST_TAG, "mailbox_dynamic_sample startup task1");
os_task_startup(task1);
}
os_task_msleep(200);
task2 = os_task_create("task2",
task2_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK2_PRIORITY);
if (task2)
{
LOG_W(TEST_TAG, "mailbox_dynamic_sample startup task2");
os_task_startup(task2);
}
}
SH_CMD_EXPORT(dynamic_mailbox, mailbox_dynamic_sample, "test dynamic mailbox");
运行结果如下:
sh>dynamic_mailbox
W/TEST: mailbox_dynamic_sample startup task1
W/TEST: task1 malloc ptr:20015cfc
W/TEST: task1 send -- name:xiaoming score:80
W/TEST: task1 malloc ptr:20015d14
W/TEST: task1 send -- name:xiaohua score:85
W/TEST: task1 malloc ptr:20015d2c
W/TEST: task1 send -- name:xiaoqiang score:90
W/TEST: mailbox_dynamic_sample startup task2
W/TEST: task2 recv -- name:xiaoming score:80
W/TEST: task2 free ptr :20015cfc
W/TEST: task2 recv -- name:xiaohua score:85
W/TEST: task2 free ptr :20015d14
W/TEST: task2 recv -- name:xiaoqiang score:90
W/TEST: task2 free ptr :20015d2c
sh>W/TEST: task1 malloc ptr:20015cfc
W/TEST: task1 send -- name:xiaoli score:95
W/TEST: task2 recv -- name:xiaoli score:95
W/TEST: task2 free ptr :20015cfc
W/TEST: task1 malloc ptr:20015cfc
W/TEST: task1 send -- name:xiaofang score:96
W/TEST: task2 recv -- name:xiaofang score:96
W/TEST: task2 free ptr :20015cfc