写给同为RT-Thread使用者以及将要学习RT-Thread的同学(三年社畜浅浅缅怀一下校内时光)
最初接触RT-Thread是在毕业实习时,19年主流实时操作系统(RTOS)还是µC/OS-II操作系统以及freertos,RT-Thread暂露头角。但在近几年RT-Thread的一跃成为现有市场装机量最大,软硬件生态最好的操作系统。它支持非常多的硬件平台,还拥有及其丰富的组件和软件包(包括文件系统、网络、IoT、AI、传感器等等),提供了便捷的开发环境和 IDE 工具,以及有众多技术文档。
RT-Thread内核实现原理:
该操作系统内核使用大量的链表以及定时器,只要掌握了这两项该操作系统内核实现就能一目了然。对于RT-Thread链表的操作详见文章(已有博主对RT-Thread双向链表进行详细讲解在此不作赘述)https://blog.csdn.net/set_mode/article/details/105900120
RT-Thread启动函数
int rtthread_startup(void)
{
rt_hw_interrupt_disable();//关中断
/* board level initalization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();//硬件初始化
/* show RT-Thread version */
rt_show_version();//打印软件信息
/* timer system initialization */
rt_system_timer_init();//系统定时器初始化
/* scheduler system initialization */
rt_system_scheduler_init();//系统调度器初始化
/* create init_thread */
rt_application_init();//应用程序初始化
/* timer thread initialization */
rt_system_timer_thread_init();//线程定时器初始化
/* idle thread initialization */
rt_thread_idle_init();//空闲线程初始化
/* start scheduler */
rt_system_scheduler_start();//开启系统调度器
/* never reach here */
return 0;
}
RT-Thread线程内部机制
**1.线程结构体rt_thread **
线程结构体内关键参数
typedef struct rt_thread *thread用来表示一个线程,它的重要成员如下:
thread->entry 函数指针
thread->parameter 函数参数
thread->stack_add 栈起始地址
thread->sp 栈顶地址
thread->init-priority 当前线程优先级
thread->init_tick 执行多少个时间片
thread->remaining_tick 剩余多少个时间片
上述这些参数在后续内核讲解中至关重要。
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object */
rt_uint8_t flags; /**< thread's flags */
rt_list_t list; /**< the object list */
rt_list_t tlist; /**< the thread list */
/* stack point and entry */
void *sp; /**< stack point */
void *entry; /**< entry */
void *parameter; /**< parameter */
void *stack_addr; /**< stack address */
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status */
/* priority */
rt_uint8_t current_priority; /**< current priority */
rt_uint8_t init_priority; /**< initialized priority */
#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
rt_ubase_t init_tick; /**< thread's initialized tick */
rt_ubase_t remaining_tick; /**< remaining tick */
struct rt_timer thread_timer; /**< built-in thread timer */
void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */
rt_uint32_t user_data; /**< private user data beyond this thread */
};
2.线程创建函数(此处以动态创建线程为例)
以RT-Thread主线程创建为例:
1.创建线程
2.启动线程
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
rt_thread_startup(tid);
线程创建函数: 初始化线程控制块参数以及在每个线程中通过rt_timer_init创建了一个线程定时器
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
struct rt_thread *thread;
void *stack_start;
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
name);
if (thread == RT_NULL)
return RT_NULL;
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
if (stack_start == RT_NULL)
{
/* allocate stack failure */
rt_object_delete((rt_object_t)thread);
return RT_NULL;
}
_rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
return thread;
}
static rt_err_t _rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
/* init thread list */
rt_list_init(&(thread->tlist));
thread->entry = (void *)entry;
thread->parameter = parameter;
/* stack init */
thread->stack_addr = stack_start;
thread->stack_size = stack_size;
/* init thread stack */
rt_memset(thread->stack_addr, '#', thread->stack_size);
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr),
(void *)rt_thread_exit);
#else
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
(void *)rt_thread_exit);
#endif
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
thread->init_priority = priority;
thread->current_priority = priority;
thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = 0;
thread->high_mask = 0;
#endif
/* tick init */
thread->init_tick = tick;
thread->remaining_tick = tick;
/* error and flags */
thread->error = RT_EOK;
thread->stat = RT_THREAD_INIT;
/* initialize cleanup function and user data */
thread->cleanup = 0;
thread->user_data = 0;
/* initialize thread timer */
rt_timer_init(&(thread->thread_timer),
thread->name,
rt_thread_timeout,
thread,
0,
RT_TIMER_FLAG_ONE_SHOT);
RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
return RT_EOK;
}
启动线程:
调用rt_thread_startup->
thread->stat = RT_THREAD_SUSPEND;将线程初始为挂起状态再通过
rt_thread_resume->
rt_schedule_insert_thread->
rt_list_remove(&(thread->tlist));->
rt_schedule_insert_thread(thread);->
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),&(thread->tlist));将线程从挂起线程链表中移除重新插入该优先级的就绪链表中(插入链表前)
然后执行rt_schedule();进行线程调度
/**
* This function will start a thread and put it to system ready queue
*
* @param thread the thread to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_startup(rt_thread_t thread)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* set current priority to initialize priority */
thread->current_priority = thread->init_priority;
/* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = thread->current_priority >> 3; /* 5bit */
thread->number_mask = 1L << thread->number;
thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */
#else
thread->number_mask = 1L << thread->current_priority;
#endif
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
thread->name, thread->init_priority));
/* change thread stat */
thread->stat = RT_THREAD_SUSPEND;
/* then resume it */
rt_thread_resume(thread);
if (rt_thread_self() != RT_NULL)
{
/* do a scheduling */
rt_schedule();
}
return RT_EOK;
}
/**
* This function will resume a thread and put it to system ready queue.
*
* @param thread the thread to be resumed
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_resume(rt_thread_t thread)
{
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: %s\n", thread->name));
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND)
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* remove from suspend list */
rt_list_remove(&(thread->tlist));
rt_timer_stop(&thread->thread_timer);
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* insert to schedule ready list */
rt_schedule_insert_thread(thread);
RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread));
return RT_EOK;
}
RT-Thread线程调度机制
1.时间片轮转
RT-Thread线程创建时我们通过线程控制块写入了两个参数
thread->init_tick 执行多少个时间片
thread->remaining_tick 剩余多少个时间片
而每个线程创建后都会有一个定时器被创建并添加链表
rt_timer_init->_rt_timer_init->rt_list_init(&(timer->row[i]));RT-Thread线程定时器处理在rt_tick_increase->rt_timer_check();(作用另一期讲解)
/**
* This function will initialize a timer, normally this function is used to
* initialize a static timer object.
*
* @param timer the static timer object
* @param name the name of timer
* @param timeout the timeout function
* @param parameter the parameter of timeout function
* @param time the tick of timer
* @param flag the flag of timer
*/
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
/* timer check */
RT_ASSERT(timer != RT_NULL);
/* timer object initialization */
rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);
_rt_timer_init(timer, timeout, parameter, time, flag);
}
接下来我们看看线程是如何被调度的,首先rt_system_scheduler_start在调度器开始调度时会找到highest_ready_priority 优先级最高的线程链表并执行相应线程。
/**
* @ingroup SystemInit
* This function will startup scheduler. It will select one thread
* with the highest priority level, then switch to it.
*/
void rt_system_scheduler_start(void)
{
register struct rt_thread *to_thread;
register rt_ubase_t highest_ready_priority;
#if RT_THREAD_PRIORITY_MAX > 32
register rt_ubase_t number;
number = __rt_ffs(rt_thread_ready_priority_group) - 1;
highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif
/* get switch to thread */
to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
struct rt_thread,
tlist);
rt_current_thread = to_thread;
/* switch to new thread */
rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
/* never come back */
}
找到操作系统系统中断可以看到每个TICK都调用了rt_tick_increase,我们可以看看该函数做了些什么事情。
可以看到在rt_tick_increase中获取了当前正在运行的线程,并且判断当前线程剩余时间片thread->remaining_tick ,当该线程时间片用完之后执行rt_thread_yield函数,该函数中所做的就是将当前线程从就绪链表中移除并将他重新放入链表尾部,并重新开始调度线程。这就是RT-Thread的时间片轮转实现机制。
/**
* This function will let current thread yield processor, and scheduler will
* choose a highest thread to run. After yield processor, the current thread
* is still in READY state.
*
* @return RT_EOK
*/
rt_err_t rt_thread_yield(void)
{
register rt_base_t level;
struct rt_thread *thread;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* set to current thread */
thread = rt_current_thread;
/* if the thread stat is READY and on ready queue list */
if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY &&
thread->tlist.next != thread->tlist.prev)
{
/* remove thread from thread list */
rt_list_remove(&(thread->tlist));
/* put thread to end of ready queue */
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_schedule();
return RT_EOK;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
2.优先级抢占
如上述机制,当存在多个优先级线程需要执行时,每个TICK都会去找到highest_ready_priority 优先级最高的线程就绪链表,当一个低优先级的线程运行时在进入TICK中断后就会转而去执行高优先级的线程,并在高优先级线程执行完毕后继续执行完剩余时间片,这就是优先级抢占机制。
下一期:RT-Thread线程间通讯
(本文章将持续输出,结合源码对RT-Thread内核实现进行分析。以上均为个人分析理解,欢迎各位在评论区指出问题。)