RT-Thread 启动流程详解

RT-Thread 启动流程图

启动流程

启动流程概述

RT-Thread 的启动流程是操作系统初始化和运行应用程序之前的关键步骤。以下是 RT-Thread 启动流程的概述:

  1. 系统启动入口:RT-Thread 规定的统一启动入口是 rtthread_startup() 函数。不同的平台和编译器可能会有不同的启动文件和入口点,但最终都会调用这个函数。

  2. 硬件初始化:在 rtthread_startup() 函数中首先进行的是硬件的初始化,这通常包括 CPU、内存、外设等的初始化。

  3. 系统堆初始化:硬件初始化之后,系统会初始化系统堆,这是为应用程序和其他软件组件提供内存分配的基础。

  4. 打印版本信息:系统会打印出 RT-Thread 的版本信息,这对于调试和确认系统状态很有帮助。

  5. 定时器初始化:系统时钟是操作系统调度的基础,因此定时器初始化是紧接着进行的步骤。

  6. 调度器初始化:调度器是操作系统的核心组件之一,负责管理线程的执行顺序和时间。

  7. 信号初始化:如果系统配置了信号支持,这一步会初始化信号相关的机制。

  8. 创建 main 线程:操作系统会创建一个用户级的 main 线程,这个线程将承载用户的应用程序代码。

  9. 定时器线程初始化:如果系统需要定时器线程来处理定时任务,这一步会进行初始化。

  10. 空闲线程初始化:空闲线程是操作系统在没有其他线程可执行时运行的线程,通常执行一些低优先级的任务或者系统维护操作。

  11. 启动调度器:一旦调度器启动,系统将根据优先级等调度策略开始调度线程的执行。

  12. 用户入口函数:最后,系统会跳转到用户定义的 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 写法的详细解释:

  1. 编译器扩展:某些编译器(如 MDK-ARM 中使用的 ARM Compiler)提供了一种扩展机制,允许开发者定义在默认入口函数 main() 之前执行的代码。这通过特殊的关键字 $Sub$$Super$ 实现。

  2. $Sub$$main 函数:这是一个在标准 main() 函数之前执行的函数。根据编译器的扩展功能,$Sub$$main 可以包含一些初始化代码,这些代码会在 main() 函数执行之前运行。

  3. $Super$$main 调用:在 $Sub$$main 函数中,可以使用 $Super$$main 来调用默认的 main() 函数。这意味着在完成了所有必要的初始化之后,控制权会传递给标准的 main() 函数。

  4. RT-Thread 系统启动:在 RT-Thread 中,$Sub$$main 常被用于在进入用户定义的 main() 函数之前初始化 RT-Thread 系统。例如,调用 rtthread_startup() 来初始化 RT-Thread 的核心组件,如定时器、调度器等。

  5. 目的:使用 $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 文档中心)

  • 16
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值