阅读引言: 因为将来工作需要, 最近在深入学习OS的内部机制,我把我觉得重要的、核心的东西分享出来, 希望对有需要的人有所帮助, 阅读此文需要读友有RTOS基础, 以及一些操作系统的基础知识, 学习过Linux的最佳, 特别是想RT-Thread适合Linux非常像的, 代码风格、IPC机制等等。
目录
一、RT-Thread中邮箱的特性
二、 邮箱的源码实现
1,邮箱控制块
2, 创建/初始化邮箱
3, 往邮箱中发送邮件
三、邮箱的简单使用
其实想深入的理解OS的内部工作机制, 无非就是涉及到一个问题, OS使用了那些数据结构组织和管理这些内核对象, 从而实现功能的。RTOS就两点链表和定时器
一、RT-Thread中邮箱的特性
消息队列的本质是链表:
空闲消息块链表:往队列里写入消息时,先从空闲链表中得到消息块;从队列读出消息后,把消息块放入空闲链表
消息块头部链表:消息写入消息块后,该消息块被放到尾部;从队列里读消息时,从头部读。
使用消息队列可以传递各类大小的消息,它使用memcpy的方式写入消息、读出消息。
如果我们只是传递很小的数据,比如一些数值,可以使用邮箱:它的效率更高。这一点就可以理解为邮箱和消息队列的区别
邮箱在内核中的实现使用的数据结构是一个循环缓冲区, 代码再后面展示
邮箱中的每一封邮件,只能容纳4字节内容(对于32位系统,指针大小刚好为4字节);发送邮件的源码如下, 邮箱中邮件的大小定死了。
邮件的发送通常是非阻塞的,线程、中断都可以发送邮件;也可使用阻塞方式发送;
邮件的接收通常是阻塞的,取决于邮箱中是否有邮件;
当一个线程向邮箱发送邮件时, 如果邮箱没满,就把数值写入邮箱中
如果邮箱满了, 发送线程可以直接返回-RT_EFULL, 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序读了邮箱,会唤醒挂起的线程。
当一个线程从邮箱接收邮件时:如果邮箱不为空,就读取邮箱中的数值, 如果邮箱为空:接收线可以直接返回-RT_ETIMOUT, 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序写了邮箱,会唤醒挂起的线程。
二、 邮箱的源码实现
使用邮箱的流程:创建/初始化邮箱、发送邮件、接收邮件、删除/脱离邮箱。
1,邮箱控制块
2, 创建/初始化邮箱
#ifdef RT_USING_HEAP
/**
* This function will create a mailbox object from system resource
*
* @param name the name of mailbox
* @param size the size of mailbox, 邮箱中邮件的数量, 每一封邮件4byte
* @param flag the flag of mailbox, 邮箱采用的等待方式, 优先级, 等待的时间大小
*
* @return the created mailbox, RT_NULL on error happen
*/
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) //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;
/* 申请邮件的空间, 根据邮件的数量 * 固定大小(4byte) */
mb->msg_pool = (rt_ubase_t *)RT_KERNEL_MALLOC(mb->size * sizeof(rt_ubase_t)); //每一封邮件大小4byte
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);
前面我们说邮箱的数据结构是一个循环缓存区, 是从源码的这个位置看出来的。
3, 往邮箱中发送邮件
/**
* This function will send a mail to mailbox object. If the mailbox is full,
* current thread will be suspended until timeout.
*
* @param mb the mailbox object
* @param value the mail
* @param timeout the waiting time
*
* @return the error code
*/
rt_err_t rt_mb_send_wait(rt_mailbox_t mb, //往哪一个邮箱中发送邮件
rt_ubase_t value, //邮件内容
rt_int32_t timeout) //超时等待时间
{
struct rt_thread *thread; //定义一个线程指针
register rt_ubase_t temp; //unsigned long类型的变量
rt_uint32_t tick_delta; //unsigned int类型的变量
/* parameter check */
RT_ASSERT(mb != RT_NULL);
RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
/* initialize delta tick */
tick_delta = 0;
/* get current thread */
thread = rt_thread_self(); //获取自己的线程控制块
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent)));
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* for non-blocking call, 邮箱中没位置 */
if (mb->entry == mb->size && timeout == 0)
{
rt_hw_interrupt_enable(temp);
return -RT_EFULL;
}
/* mailbox is full 当邮箱中没位置时, 设置了超时时间的情况 */
while (mb->entry == mb->size)
{
/* reset error number in thread */
thread->error = RT_EOK;
/* no waiting, return timeout */
if (timeout == 0)
{
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return -RT_EFULL;
}
RT_DEBUG_IN_THREAD_CONTEXT;
/* suspend current thread */
rt_ipc_list_suspend(&(mb->suspend_sender_thread),
thread,
mb->parent.parent.flag);
/* has waiting time, start thread timer */
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));
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* re-schedule */
rt_schedule();
/* resume from suspend state */
if (thread->error != RT_EOK)
{
/* return error */
return thread->error;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* if it's not waiting forever and then re-calculate timeout tick */
if (timeout > 0)
{
tick_delta = rt_tick_get() - tick_delta;
timeout -= tick_delta;
if (timeout < 0)
timeout = 0;
}
}
/* set ptr */
mb->msg_pool[mb->in_offset] = value;
/* increase input offset */
++ mb->in_offset;
if (mb->in_offset >= mb->size)
mb->in_offset = 0;
if(mb->entry < RT_MB_ENTRY_MAX)
{
/* increase message entry */
mb->entry ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
/* resume suspended thread */
if (!rt_list_isempty(&mb->parent.suspend_thread))
{
rt_ipc_list_resume(&(mb->parent.suspend_thread));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
rt_schedule();
return RT_EOK;
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return RT_EOK;
}
RTM_EXPORT(rt_mb_send_wait);