之所以引入线程间通信,是为了实现互斥,休眠-唤醒。
队列可以指定消息的大小、个数,存放消息,取出消息时都是由rt_memcpy()实现。
邮箱
保存数据的核心在于数组,只能存放unsigned long类型数据,数据存取、读出,直接赋值即可,使传递小数据时,效率更高。
核心是链表、定时器、环形Buffer
避免两个线程同时写:关中断。
定时器超时函数,设置线程的错误码为ETIMEOUT,并将自己重新放入就绪链表;
thread->error = -RT_ETIMEOUT;
struct rt_mailbox
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_ubase_t *msg_pool; /**< start address of message buffer */
rt_uint16_t size; /**< size of message pool */
rt_uint16_t entry; /**< index of messages in msg_pool */
rt_uint16_t in_offset; /**< input offset of the message buffer */
rt_uint16_t out_offset; /**< output offset of the message buffer */
rt_list_t suspend_sender_thread; /**< sender thread suspended on this mailbox */
};
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;
/* allocate object */
mb = (rt_mailbox_t)rt_object_allocate(RT_Object_Class_MailBox, name); //分配一个邮箱结构体
if (mb == RT_NULL)
return mb;
/* set parent */
mb->parent.parent.flag = flag;
/* initialize ipc object */
rt_ipc_object_init(&(mb->parent));
/* initialize mailbox */
mb->size = size; //邮箱大小
mb->msg_pool = (rt_ubase_t *)RT_KERNEL_MALLOC(mb->size * sizeof(rt_ubase_t)); //邮箱大小*(unsigned long=4)
if (mb->msg_pool == RT_NULL)
{
/* delete mailbox object */
rt_object_delete(&(mb->parent.parent));
return RT_NULL;
}
mb->entry = 0;
mb->in_offset = 0;
mb->out_offset = 0;
/* initialize an additional list of sender suspend thread */
rt_list_init(&(mb->suspend_sender_thread));
return mb;
}
RTM_EXPORT(rt_mb_create);
读数据
/* disable interrupt */
temp = rt_hw_interrupt_disable(); //关中断
if (mb->entry == 0 && timeout == 0) //没有数据且不愿意等待
{
rt_hw_interrupt_enable(temp);
return -RT_ETIMEOUT;
}
//没有数据,且愿意等待
rt_ipc_list_suspend(&(mb->parent.suspend_thread),
thread,
mb->parent.parent.flag);//从ready链表移出,挂到parent.suspend_thread链表
*value = mb->msg_pool[mb->out_offset]; //有数据从out_offset读取
//将读数据索引加+1,如果达到了最大值,又从0开始。
++ mb->out_offset;
if (mb->out_offset >= mb->size)
mb->out_offset = 0;
mb->entry --;//数据数量减少
if (!rt_list_isempty(&(mb->suspend_sender_thread))) //释放等待发送数据链表中的第一个线程
{
rt_ipc_list_resume(&(mb->suspend_sender_thread));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent)));
rt_schedule();
return RT_EOK;
}
发送数据
temp = rt_hw_interrupt_disable();
if (mb->entry == mb->size && timeout == 0)//邮箱已满,不愿意等待
{
rt_hw_interrupt_enable(temp);
return -RT_EFULL;
}
//邮箱满了,愿意等待
while (mb->entry == mb->size)
{
rt_ipc_list_suspend(&(mb->suspend_sender_thread),
thread,
mb->parent.parent.flag); //从就绪链表移除,将自己挂在发送线程链表上。
if (timeout > 0)
{
/* get the start tick of timer */
tick_delta = rt_tick_get();
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n",
thread->name));
/* reset the timeout of thread timer and start it */
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout);
rt_timer_start(&(thread->thread_timer));
}
...
rt_schedule();//让出CPU资源
}
//邮箱有空闲位置
mb->msg_pool[mb->in_offset] = value; //写数据
定时器检查
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));
current_tick = rt_tick_get();
/* disable interrupt */
level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))//取出定时器
{
t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) //判断时间是否到了
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
/* remove timer from timer list firstly */
_rt_timer_remove(t);
/* call timeout function */
t->timeout_func(t->parameter); //时间到了调用定时器函数
/* re-get tick */
current_tick = rt_tick_get();
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else
break;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
}