RT-Thread 启动流程图
启动流程概述
RT-Thread 的启动流程是操作系统初始化和运行应用程序之前的关键步骤。以下是 RT-Thread 启动流程的概述:
-
系统启动入口:RT-Thread 规定的统一启动入口是
rtthread_startup()
函数。不同的平台和编译器可能会有不同的启动文件和入口点,但最终都会调用这个函数。 -
硬件初始化:在
rtthread_startup()
函数中首先进行的是硬件的初始化,这通常包括 CPU、内存、外设等的初始化。 -
系统堆初始化:硬件初始化之后,系统会初始化系统堆,这是为应用程序和其他软件组件提供内存分配的基础。
-
打印版本信息:系统会打印出 RT-Thread 的版本信息,这对于调试和确认系统状态很有帮助。
-
定时器初始化:系统时钟是操作系统调度的基础,因此定时器初始化是紧接着进行的步骤。
-
调度器初始化:调度器是操作系统的核心组件之一,负责管理线程的执行顺序和时间。
-
信号初始化:如果系统配置了信号支持,这一步会初始化信号相关的机制。
-
创建 main 线程:操作系统会创建一个用户级的 main 线程,这个线程将承载用户的应用程序代码。
-
定时器线程初始化:如果系统需要定时器线程来处理定时任务,这一步会进行初始化。
-
空闲线程初始化:空闲线程是操作系统在没有其他线程可执行时运行的线程,通常执行一些低优先级的任务或者系统维护操作。
-
启动调度器:一旦调度器启动,系统将根据优先级等调度策略开始调度线程的执行。
-
用户入口函数:最后,系统会跳转到用户定义的
main()
函数,用户可以在该函数中编写自己的应用程序逻辑。
在整个启动流程中,RT-Thread 利用了多个初始化函数,这些函数通过宏定义的方式在系统启动时自动被调用,形成了自动初始化机制。这样,开发者不需要显式调用每个初始化函数,简化了系统的启动过程。
启动流程结束后,RT-Thread 将进入正常的运行状态,开始执行用户应用程序。这个流程确保了系统的各个组件被正确地初始化,为后续的稳定运行打下了基础。
源码分析
C代码入口
startup_xx.s这里不做解释,MCU启动后会从汇编运行跳转到C代码,下面是源码 components.c 中关于C代码入口的定义
/*
* 根据不同的编译器定义,重定义或定义 main 函数以启动 RT-Thread 线程。
*/
#if defined(__ARMCC_VERSION)
/* 当使用 ARMCC 编译器时,重定义 main 函数为 $Sub$$main */
extern int $Super$$main(void);
int $Sub$$main(void)
{
rtthread_startup(); /* 启动 RT-Thread */
return 0;
}
#elif defined(__ICCARM__)
/* 当使用 IAR 编译器时,定义 __low_level_init 函数以在初始化阶段调用 RT-Thread */
extern int main(void);
extern void __iar_data_init3(void);
int __low_level_init(void)
{
__iar_data_init3(); /* 调用 IAR 的数据初始化函数 */
rtthread_startup(); /* 启动 RT-Thread */
return 0;
}
#elif defined(__GNUC__)
/* 当使用 GCC 编译器时,定义 entry 函数作为程序入口 */
int entry(void)
{
rtthread_startup(); /* 启动 RT-Thread */
return 0;
}
#endif
以MDK(ARMCC)编译器为例$Sub$$main
是一种特殊的函数命名约定,它通常与一些编译器的扩展特性有关。这种命名约定允许在程序的标准入口点 main()
函数之前执行一些初始化代码。下面是 $Sub$$main
写法的详细解释:
-
编译器扩展:某些编译器(如 MDK-ARM 中使用的 ARM Compiler)提供了一种扩展机制,允许开发者定义在默认入口函数
main()
之前执行的代码。这通过特殊的关键字$Sub$
和$Super$
实现。 -
$Sub$$main
函数:这是一个在标准main()
函数之前执行的函数。根据编译器的扩展功能,$Sub$$main
可以包含一些初始化代码,这些代码会在main()
函数执行之前运行。 -
$Super$$main
调用:在$Sub$$main
函数中,可以使用$Super$$main
来调用默认的main()
函数。这意味着在完成了所有必要的初始化之后,控制权会传递给标准的main()
函数。 -
RT-Thread 系统启动:在 RT-Thread 中,
$Sub$$main
常被用于在进入用户定义的main()
函数之前初始化 RT-Thread 系统。例如,调用rtthread_startup()
来初始化 RT-Thread 的核心组件,如定时器、调度器等。 -
目的:使用
$Sub$$main
的主要目的是为了简化系统的初始化过程,让开发者可以专注于应用程序的实现,而不必担心复杂的系统初始化细节。
值得注意的是,这种写法是特定编译器的扩展特性,并不是所有的编译器都支持。在使用时需要确保你的开发环境支持这种扩展,否则可能会出现编译错误。
rtthread_startup
/*
* 函数名称:rtthread_startup
* 功能描述:启动RT-Thread操作系统。
* 参数说明:无。
* 返回值:始终返回0。
*/
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
/* 信号系统初始化 */
rt_system_signal_init();
#endif /* RT_USING_SIGNALS */
/* 创建初始化线程 */
rt_application_init();
/* 定时器线程初始化 */
rt_system_timer_thread_init();
/* 空闲线程初始化 */
rt_thread_idle_init();
#ifdef RT_USING_SMP
/* 启用多处理器系统下的spinlock */
rt_hw_spin_lock(&_cpus_lock);
#endif /* RT_USING_SMP */
/* 启动调度器 */
rt_system_scheduler_start();
/* 此处代码不可达 */
return 0;
}
rt_application_init
/**
* 初始化应用程序。
* 本函数用于创建并初始化主线程。根据是否定义了RT_USING_HEAP宏,选择不同的线程创建方式:
* 若定义了RT_USING_HEAP,则使用rt_thread_create函数动态创建主线程;
* 若未定义RT_USING_HEAP,则使用预分配的栈空间静态创建主线程。
*/
void rt_application_init(void)
{
rt_thread_t tid;
#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);
#else
rt_err_t result;
/* 使用静态分配方式初始化主线程 */
tid = &main_thread;
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
main_thread_stack, sizeof(main_thread_stack), RT_MAIN_THREAD_PRIORITY, 20);
/* 确保线程初始化成功 */
RT_ASSERT(result == RT_EOK);
/* 用于消除未使用变量的编译警告 */
(void)result;
#endif /* RT_USING_HEAP */
/* 启动创建的主线程 */
rt_thread_startup(tid);
}
该函数用于初始化并启动一个名为"main"
的主线程。若定义了RT_USING_HEAP
宏,则使用堆内存动态创建线程;否则使用静态内存。通过调用rt_thread_create()
或rt_thread_init()
设置线程属性(如入口函数、栈大小、优先级等),并确保创建成功。最后,调用rt_thread_startup()
启动主线程。
main_thread_entry
/**
* @brief 主线程入口函数
*
* 该函数是系统的主线程入口,它首先初始化RT-Thread组件,然后根据编译器的不同调用系统的主要函数。
*
* @param parameter 函数参数,未使用
*/
void main_thread_entry(void *parameter)
{
extern int main(void); // 声明main函数
#ifdef RT_USING_COMPONENTS_INIT
/* 初始化RT-Thread组件 */
rt_components_init();
#endif /* RT_USING_COMPONENTS_INIT */
#ifdef RT_USING_SMP
/* 在多核系统中启动Secondary CPU */
rt_hw_secondary_cpu_up();
#endif /* RT_USING_SMP */
/* 调用系统main函数 */
#ifdef __ARMCC_VERSION
{
extern int $Super$$main(void); // 声明ARMCC编译器的main函数
$Super$$main(); /* 专用于ARMCC编译器的main函数调用 */
}
#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__) || defined(__TI_COMPILER_VERSION__)
main(); // 适用于其他常见编译器的main函数调用
#endif
}
main_thread_entry
是RT-Thread操作系统的主线程入口。它负责初始化RT-Thread组件,根据编译器类型正确调用系统主函数(如main
或$Super$$main
),以启动操作系统主循环。
本文引用及参考资料
[引用及参考资料](RT-Thread 文档中心)