在一些不应用操作系统的单片机软件工程外面,除了汇编启动文件之外,普遍认为程序入口就是main函数,很多程序代码都是从main函数开始进行剖析的。
而对于RT-Thread实时操作系统,程序在跑到main函数之前,其实是进行了一系列的启动流程初始化工作,而这些初始化操作是针对RT-Thread内核和具体的板卡进行的,用户不须要干涉这个启动流程。
在进入main函数之前,RT-Thread进行了如图所示的启动操作。
不带操作系统的单片机程序,个别都会从启动文件startup_xx.s间接跳转到main函数开始执行,而带RT-Thread操作系统的程序,在进入main函数之前,还进行了如上图所示的一系列操作。以上的操作看似简单繁多,但其实次要是在调用main函数之前,调用了rtthread_startup函数。对于如何在调用main函数之前,调用rtthread_startup函数,不同的编译器有不同的操作。
对于MDK编译器,次要是应用了MDK的扩大性能 $Sub和$Super
,而对于IAR编译器,则是通过__low_level_init()函数,对于GCC编译器,则是通过entry函数,这些函数都是会在调用main函数之前被调用的。
以MDK编译器为例,给main函数增加一个 $Sub$$ 前缀,就造成了一个新的性能函数,这个性能函数会在调用main函数之前被调用,这是MDK编译器所规定的,具体能够查看以下链接:ARM® Compiler v5.06 for µVision®armlink User Guide
对于程序从启动文件跳转到main函数入口的关系,总结概括如下图所示。
在$Sub$$main函数外面,次要是调用了rtthread_startup()函数,这个函数是RT-Thread规定的对立启动入口,这个函数次要进行了如图所示的一系列初始化工作。
以下是对于rtthread_startup()函数外面各个函数的具体阐明。
1.对于rt_hw_board_init()函数,次要是初始化了中断向量表,实现了零碎时钟的初始化,如果有应用到零碎组件的话,同时初始化零碎组件,并且设置打印信息的输入控制台,同时初始化零碎堆内存,程序代码如下图所示。
2.对于rt_show_version()函数,次要是在信息控制台初始化胜利后,打印RT-Thread内核的零碎版本信息,这个函数的具体实现,如下图所示。
3.对于rt_system_timer_init()和rt_system_scheduler_init()函数,次要是初始化了零碎定时器链表和RT-Thread系统调度器,因为调度器的实现原理略为简单,此处暂不开展阐述。
4.对于rt_application_init()函数,次要是创立了一个名为main的主线程,这个线程的函数入口是main_thread_entry,这里有两种创立形式,二选一,如果应用了零碎堆内存,则应用动态创建的形式,线程应用的内存资源能够动静进行申请或开释,如果没有应用零碎堆内存,则应用动态创立的形式,线程应用的内存资源是固定好的,不能被开释,函数实现如下图所示。
5.对于rt_system_timer_thread_init()函数,次要是初始化软件定时器的列表,并且采纳动态形式创立一个名为timer的软件定时器,并且把软件定时器线程放入调度器外面,函数实现如下图所示。
6.对于rt_thread_idle_init()函数,次要是依据芯片CPU的数量,应用动态形式创立闲暇线程,实际上,闲暇线程并不闲暇,这个线程在零碎没有任何用户线程调度的时候,就会被调度起来,这个闲暇线程次要是查看零碎有没有曾经沦亡的线程,如果有,则把沦亡线程的资源进行回收,如果零碎使能了电源治理,则会让零碎进行低功耗模式,函数的具体实现,如下图所示。
7.对于rt_system_scheduler_start()函数,次要是开始使能操作系统调度器,调度器启动后,会依据零碎的调度规定,从线程就绪列表外面,抉择优先级最高的线程进行启动。
8.从以上剖析可知,RT-Thread零碎在启动的时候,至多会启动一个main主线程和一个idle闲暇线程,如果系统配置有使能软件定时器,还会启动一个timer定时器线程,也就是说,零碎一旦启动后,就会有两个(或三个)线程在进行调度,如下图所示。
感激浏览!
原文链接:https://club.rt-thread.org/as...