1、邮箱机制
可以将邮箱看做一个环形队列。放入邮件的操作等同于入队。取出邮件的操作等同于出队。entry是队列条目大小,in_offset是队头,out_offset是队尾,size是队列大小。
2、邮箱控制块
邮箱控制块是用于管理邮箱的一个数据结构,由结构体 struct rt_mailBox表示。
struct rt_mailBox
{
struct rt_ipc_object parent; /* ipc对象 */
rt_uint32_t *msg_pool; /* 邮箱缓冲区的起始地址 */
rt_uint16_t size; /* 邮箱缓冲区的大小 */
rt_uint16_t entry; /* 邮箱中邮件的数目 */
rt_uint16_t in_offset; /* 邮箱缓冲的尾指针 */
rt_uint16_t out_offset; /* 邮箱缓冲的头指针 */
rt_list_t suspend_sender_thread; /* 因等待发送邮件而挂起的线程链表 */
};
typedef struct rt_mailBox *rt_mailBox_t;
3、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)
{
RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name); //初始化邮箱对象
mb->parent.parent.flag = flag; //设置邮箱标志
rt_ipc_object_init(&(mb->parent)); //初始化邮箱ipc对象
/* 初始化邮箱 */
mb->msg_pool = msgpool; //设置邮箱msg_pool为msgpool
mb->size = size; //设置邮箱size为size
mb->entry = 0; //设置邮箱entry为0
mb->in_offset = 0; //设置邮箱in_offset为0
mb->out_offset = 0; //设置邮箱out_offset为0
rt_list_init(&(mb->suspend_sender_thread)); //初始化邮箱发送挂起线程链表
return RT_EOK; //返回RT_EOK
}
4、rt_mb_create()函数
此函数将从内存中分配邮箱对象。
rt_mailBox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)
{
rt_mailBox_t mb;
RT_DEBUG_NOT_IN_INTERRUPT; //断言不在ISR中调用
mb = (rt_mailBox_t)rt_object_allocate(RT_Object_Class_MailBox, name); //分配邮箱对象
if (mb == RT_NULL) //mb等于RT_NULL
return mb; //返回RT_NULL
mb->parent.parent.flag = flag; //设置邮箱标志
rt_ipc_object_init(&(mb->parent)); //初始化邮箱ipc对象
/* 初始化邮箱 */
mb->size = size; //设置邮箱的size为size
mb->msg_pool = RT_KERNEL_MALLOC(mb->size * sizeof(rt_uint32_t)); //分配内存
if (mb->msg_pool == RT_NULL) //分配内存失败
{
rt_object_delete(&(mb->parent.parent)); //删除邮箱对象
return RT_NULL; //返回RT_NULL
}
mb->entry = 0; //设置邮箱的entry为0
mb->in_offset = 0; //设置邮箱的in_offset为0
mb->out_offset = 0; //设置邮箱的out_offset为0
rt_list_init(&(mb->suspend_sender_thread)); //初始化邮箱发送挂起线程链表
return mb;
}
5、rt_mb_detach()函数
此函数将将邮箱与资源管理分离。
rt_err_t rt_mb_detach(rt_mailBox_t mb)
{
/* 参数检查 */
RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象是邮箱
RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent)); //断言邮箱是静态的
rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); //唤醒因获取邮箱资源而挂起的所有线程
rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); //唤醒邮箱发送挂起线程链表上的所有线程
rt_object_detach(&(mb->parent.parent)); //脱离邮箱对象
return RT_EOK; //返回RT_EOK
}
6、rt_mb_delete()函数
rt_err_t rt_mb_delete(rt_mailBox_t mb)
{
RT_DEBUG_NOT_IN_INTERRUPT; //断言不在ISR中调用
/* 参数检查 */
RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象为邮箱对象
RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE); //断言邮箱不是静态的
rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); //唤醒因获取邮箱资源而挂起的所有线程
rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); //唤醒邮箱发送挂起线程链表上的所有线程
RT_KERNEL_FREE(mb->msg_pool); //释放邮箱资源
rt_object_delete(&(mb->parent.parent)); //删除邮箱对象
return RT_EOK; //返回RT_EOK
}
7、rt_mb_send_wait()函数
该函数将向邮箱对象发送一封邮件。如果邮箱已满,则当前线程将挂起直到超时。
rt_err_t rt_mb_send_wait(rt_mailBox_t mb,
rt_uint32_t value,
rt_int32_t timeout)
{
struct rt_thread *thread;
register rt_ubase_t temp;
rt_uint32_t tick_delta;
/* 参数检查 */
RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象是邮箱
tick_delta = 0; //将tick_delta设置为0
thread = rt_thread_self(); //获取当前线程
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent))); //调用回调
temp = rt_hw_interrupt_disable(); //关中断
/* 对于非阻塞调用 */
if (mb->entry == mb->size && timeout == 0) //邮箱满了
{
rt_hw_interrupt_enable(temp); //开中断
return -RT_EFULL; //返回-RT_EFULL
}
while (mb->entry == mb->size) //邮箱满了
{
thread->error = RT_EOK; //将线程的错误码设置为RT_EOK
if (timeout == 0) //等待超时
{
rt_hw_interrupt_enable(temp); //开中断
return -RT_EFULL; //返回-RT_EFULL
}
RT_DEBUG_IN_THREAD_CONTEXT; //上下文检查
rt_ipc_list_suspend(&(mb->suspend_sender_thread),
thread,
mb->parent.parent.flag); //挂起当前线程
/* 需要等待,启动线程定时器 */
if (timeout > 0)
{
tick_delta = rt_tick_get(); //获取当前时间
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n",
thread->name)); //打印信息
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout); //设置线程定时器超时时间
rt_timer_start(&(thread->thread_timer)); //启动线程定时器
}
rt_hw_interrupt_enable(temp); //开中断
rt_schedule(); //调度
if (thread->error != RT_EOK) //线程的错误码不等于RT_EOK
{
return thread->error; //返回错误码
}
temp = rt_hw_interrupt_disable(); //关中断
/* 重新计算超时时间 */
if (timeout > 0)
{
tick_delta = rt_tick_get() - tick_delta; //计算从设置线程定时器起到现在经过的时间
timeout -= tick_delta; //计算剩余的超时时间
if (timeout < 0)
timeout = 0; //如果超时时间小于0,设置超时时间等于0
}
}
/* 队列操作,入队 */
mb->msg_pool[mb->in_offset] = value; //放入邮件
++ mb->in_offset; //邮箱in_offset加1
if (mb->in_offset >= mb->size) //邮箱in_offset大于size
mb->in_offset = 0; //设置邮箱in_offset为0
mb->entry ++; //邮箱条目加1
/* 唤醒挂起线程 */
if (!rt_list_isempty(&mb->parent.suspend_thread)) //邮箱挂起线程链表非空
{
rt_ipc_list_resume(&(mb->parent.suspend_thread)); //唤醒挂起线程
rt_hw_interrupt_enable(temp); //开中断
rt_schedule(); //调度
return RT_EOK; //返回RT_EOK
}
rt_hw_interrupt_enable(temp); //开中断
return RT_EOK; //返回RT_EOK
}
8、rt_mb_send()函数
rt_err_t rt_mb_send(rt_mailBox_t mb, rt_uint32_t value)
{
return rt_mb_send_wait(mb, value, 0); //超时参数为0
}
9、rt_mb_recv()函数
此函数将接收来自邮箱的邮件,如果邮箱对象中没有邮件,线程将等待指定的时间。
rt_err_t rt_mb_recv(rt_mailBox_t mb, rt_uint32_t *value, rt_int32_t timeout)
{
struct rt_thread *thread;
register rt_ubase_t temp;
rt_uint32_t tick_delta;
/* 参数检查 */
RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象是邮箱
tick_delta = 0; //设置tick_delta等于0
thread = rt_thread_self(); //获取当前线程
RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mb->parent.parent))); //调用回调
temp = rt_hw_interrupt_disable(); //关中断
/* 对于非阻塞调用 */
if (mb->entry == 0 && timeout == 0) //邮箱为空
{
rt_hw_interrupt_enable(temp); //开中断
return -RT_ETIMEOUT; //返回-RT_ETIMEOUT
}
while (mb->entry == 0) //邮箱为空
{
thread->error = RT_EOK; //设置线程错误码为RT_EOK
if (timeout == 0) //等待超时
{
rt_hw_interrupt_enable(temp); //开中断
thread->error = -RT_ETIMEOUT; //设置线程错误码为-RT_ETIMEOUT
return -RT_ETIMEOUT; //返回-RT_ETIMEOUT
}
RT_DEBUG_IN_THREAD_CONTEXT; //上下文检查
rt_ipc_list_suspend(&(mb->parent.suspend_thread),
thread,
mb->parent.parent.flag); //挂起当前线程
/* 需等待,开启线程定时器 */
if (timeout > 0)
{
tick_delta = rt_tick_get(); //获取当前时间
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_recv: start timer of thread:%s\n",
thread->name)); //打印信息
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout); //设置线程定时器超时时间
rt_timer_start(&(thread->thread_timer)); //启动线程定时器
}
rt_hw_interrupt_enable(temp); //开中断
rt_schedule(); //调度
if (thread->error != RT_EOK) //线程错误码不等于RT_EOK
{
return thread->error; //返回线程错误码
}
temp = rt_hw_interrupt_disable(); //关中断
/* 重新计算超时时间 */
if (timeout > 0)
{
tick_delta = rt_tick_get() - tick_delta; //计算从设置线程定时器起到现在经过的时间
timeout -= tick_delta; //计算剩余超时时间
if (timeout < 0) //如果超时时间小于0
timeout = 0; //设置超时时间为0
}
}
/* 队列操作,出队 */
*value = mb->msg_pool[mb->out_offset]; //取出邮件
++ mb->out_offset; //设置邮箱out_offset加1
if (mb->out_offset >= mb->size) //设置邮箱out_offset大于mb->size
mb->out_offset = 0; //设置邮箱out_offset为0
mb->entry --; //邮箱条目减1
/* 唤醒挂起线程 */
if (!rt_list_isempty(&(mb->suspend_sender_thread))) //邮箱发送挂起线程非空
{
rt_ipc_list_resume(&(mb->suspend_sender_thread)); //唤醒邮箱发送挂起线程
rt_hw_interrupt_enable(temp); //开中断
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); //调用回调函数
rt_schedule(); //调度
return RT_EOK; //返回RT_EOK
}
rt_hw_interrupt_enable(temp); //开中断
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); //调用回调函数
return RT_EOK; //返回RT_EOK
}
10、rt_mb_control()函数
rt_err_t rt_mb_control(rt_mailBox_t mb, int cmd, void *arg)
{
rt_ubase_t level;
/* 参数检查 */
RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象为邮箱
if (cmd == RT_IPC_CMD_RESET)
{
level = rt_hw_interrupt_disable(); //关中断
rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); //唤醒因获取邮件而等待的所有线程
rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); //唤醒因发送邮件而等待的所有线程
/* 初始化邮箱 */
mb->entry = 0; //设置邮箱entry为0
mb->in_offset = 0; //设置邮箱in_offset为0
mb->out_offset = 0; //设置邮箱out_offset为0
rt_hw_interrupt_enable(level); //开中断
rt_schedule(); //调度
return RT_EOK; //返回RT_EOK
}
return -RT_ERROR; //返回-RT_ERROR
}
原文地址:https://www.jb51.cc/wenti/3281297.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。