RT-Thread 学习记录之内核启动流程
内核简介
内核是学习 RT-Thread 最基础,也是最重要的部分。RT-Thread 中,内核处于硬件层之上,内核部分包括内核库、实时内核实现。
从上图可知,RT-Thread 内核库中包含了对象管理、实时调度器、线程管理、线程通信、时钟管理、内存管理、设备管理等功能实现的相关源码。
这些后面都会做相对应的专题分析,首先我们应该先了解 RT-Thread 的启动流程。
启动顺序
以 CH32V307 开发板为例,通过调试功能进入启动,可整理出如下顺序:
可知,开发板上电后,执行启动文件 .S 的内容进行必要的初始化,之后进入 int entry(void) 这个函数(由于该开发板环境使用 GCC 编译,并未进入 S u b Sub Sub$main 函数),之后由这个函数跳入 rtthread_startup() 函数进行 RT-Thread 系统的初始化和启动。在执行完 rtthread_startup() 函数后,会创建 main 线程,并开启任务调度器,由此进入 main.c 中的用户主函数。
源码简析
首先对 rtthread_startup 函数进行分析:
int rtthread_startup(void)
{
/* 清除所有硬件中断 */
rt_hw_interrupt_disable();
/* 时钟、堆栈、外设等初始化 */
rt_hw_board_init();
/* 打印 RT-Thread 版本信息等 */
rt_show_version();
/* 初始化定时器 */
rt_system_timer_init();
/* 初始化调度器 */
rt_system_scheduler_init();
/* 判断是否使用信号系统,由此选择是否初始化 */
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif /* RT_USING_SIGNALS */
/* 创建原始线程, main 线程 */
rt_application_init();
/* 创建定时器线程 */
rt_system_timer_thread_init();
/* 创建空闲线程 */
rt_thread_idle_init();
/* 判断是否使用多核处理器,由此选择是否初始化 */
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /* RT_USING_SMP */
/* 启动调度器 */
rt_system_scheduler_start();
/* 永不抵达 */
return 0;
}
对 rtthread_startup 函数进行简单分析可知,在该函数中,RT-Thread 完成了对整个系统内核的初始化以及调度器的启动。
接下来跳过各种内核初始化后对 rt_application_init 函数进行分析:
void rt_application_init(void)
{
rt_thread_t tid;//线程句柄
/* 创建/初始化 main 线程 */
#ifdef RT_USING_HEAP
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);//动态创建 main 线程
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(result == RT_EOK);//静态初始化 main 线程
/* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif /* RT_USING_HEAP */
rt_thread_startup(tid);//启动 main 线程
}
在 rt_application_init 函数中,系统创建并启动了 main 线程,其入口函数是 main_thread_entry :
void main_thread_entry(void *parameter)
{
extern int main(void);//声明 main 函数
/* 初始化系统组件 */
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif /* RT_USING_COMPONENTS_INIT */
/* 判断是否使用多核 */
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif /* RT_USING_SMP */
/* invoke system main function */
#ifdef __ARMCC_VERSION
{
extern int $Super$$main(void);
$Super$$main(); /* for ARMCC. */
}
#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__)
main();//进入 main 函数
#endif
}
可知在这一步进行了主函数的调用。
总结
以上是 RT-Thread 的内核启动流程,在不同的芯片间大同小异,比如在 STM32 系列单片机中,进入 rtthread_startup 函数的入口为 $Sub $ $main 函数,而不是 int entry(void) 这个函数,其主要原因是编译链的不同,在通常使用 STM32 系列芯片时是使用 MDK 的 ARMCC 编译链进行编译的,而我手里这款 CH32V307 采用的是 GCC 编译链。
RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。