RT-Thread 的启动流程(学习笔记)

本文参考自[野火EmbedFire]《RT-Thread内核实现与应用开发实战——基于STM32》,仅作为个人学习笔记。更详细的内容和步骤请查看原文(可到野火资料下载中心下载)

当你拿到一个移植好的 RT-Thread 工程的时候,你去看 main 函数,只能在 main 函数里面看到创建线程和启动线程的代码,硬件初始化,系统初始化,启动调度器等信息都看不到。那是因为RT-Thread 拓展了 main 函数,在 main 函数之前把这些工作都做好了。

——原文

Reset_Handler 复位函数

系统上电后第一个函数是启动文件中的复位函数,这个启动文件由汇编语言编写,复位函数会调用C库函数__main,该函数的主要工作时初始化系统的堆和栈,最后调用main()函数,进入C语言的世界。

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

$Sub$$main 函数

原文说单步执行完__main之后,会跳转到component.c中的$Sub$$main()函数,但我单步到__main后,直接到了main()函数(这可能是我操作不到位吧)。

在Keil的编译器中,存在$Sub$$$Super$$这样一对符号,它们可以扩展一个函数,比如使用$Sub$$main()可以在执行main()之前执行$Sub$$main(),然后调用$Super$$main()来切换到main()函数中。

下面是component.c中的部分函数(针对Keil编译器)

extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
    
    
    rtthread_startup();
    return 0;
}

/* the system main thread */
void main_thread_entry(void *parameter)
{
    
    
    extern int main(void);
    extern int $Super$$main(void);

#ifdef RT_USING_COMPONENTS_INIT
    /* RT-Thread components initialization */
    rt_components_init();
#endif
    /* invoke system main function */
    $Super$$main(); /* for ARMCC. */
}

rtthread_startup() 函数

上一步$Sub$$main()函数中只有rtthread_startup()这么一个函数,RT-Thread的所有初始化都在这个函数里实现。

其中rt_hw_board_init()这个函数我们已经接触到多次,定义在board.c中,用来初始化底层固件。

int rtthread_startup(void)
{
    
    
    rt_hw_interrupt_disable();

    /* board level initialization
     * 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_application_init() 函数

rt_application_init()函数里创建了一个线程,这是一个初始线程,当所有应用线程都成功创建好后,初始线程就把自己关闭。函数最后一行调用了rt_thread_startup()函数,虽然我们知道这个函数的作用是启动线程,但这个时候线程并没有启动,因为在上层函数rtthread_startup()最后一行(见上文)的rt_system_scheduler_start()还未执行,系统的线程还未开始调度。

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_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);
    RT_ASSERT(result == RT_EOK);

    /* if not define RT_USING_HEAP, using to eliminate the warning */
    (void)result;
#endif

    rt_thread_startup(tid);
}

main_thread_entry() 函数

下面是初始线程的入口函数,主要作用是进行组件初始化,最后调用$Super$main()回到main()函数中。

#ifndef RT_USING_HEAP
/* if there is not enable heap, we should use static thread and stack. */
ALIGN(8)
static rt_uint8_t main_stack[RT_MAIN_THREAD_STACK_SIZE];
struct rt_thread main_thread;
#endif

/* the system main thread */
void main_thread_entry(void *parameter)
{
    
    
    extern int main(void);
    extern int $Super$$main(void);

#ifdef RT_USING_COMPONENTS_INIT
    /* RT-Thread components initialization */
    rt_components_init();
#endif
    /* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
    $Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}

main() 函数

main()函数是通过初始线程调用的,前面提到初始线程在后面需要自己关闭,意思就是main()函数运行完之后,整个系统就和main()无关了。这里和裸机系统差别很大,裸机系统的main()函数里都有一个while(1),所以单片机上电后main()函数就没有停止运行过,而RT-Thread(以及一些其他的RTOS)的main()是不包含死循环的,取而代之的是一堆线程的创建和初始化。


到这里RT-Thread的整个启动流程就基本介绍完了,关于main()运行结束后系统是如何运作的,这里就不作论述。

猜你喜欢

转载自blog.csdn.net/weixin_43772810/article/details/123656826