RT-Thread 自動初始化機制

RT-Thread 自動初始化機制

1、自動初始化機制簡介

在這裏插入圖片描述
在系統啓動流程圖中,有兩個函數:rt_components_board_init() 與 rt_components_init(),其後的帶底色方框內部的函數表示被自動初始化的函數,其中:

  1. “board init functions” 爲所有通過 INIT_BOARD_EXPORT(fn) 申明的初始化函數。
  2. “pre-initialization functions” 爲所有通過 INIT_PREV_EXPORT(fn)申明的初始化函數。
  3. “device init functions” 爲所有通過 INIT_DEVICE_EXPORT(fn) 申明的初始化函數。
  4. “components init functions” 爲所有通過 INIT_COMPONENT_EXPORT(fn)申明的初始化函數。
  5. “enviroment init functions” 爲所有通過 INIT_ENV_EXPORT(fn) 申明的初始化函數。
  6. “application init functions” 爲所有通過 INIT_APP_EXPORT(fn)申明的初始化函數
    用來實現自動初始化功能的宏接口定義詳細描述如下表所示:
    在這裏插入圖片描述

2、自動初始化機制原理

RT-Thread 的自動初始化機制使用了自定義 RTI 符號段,將需要在啓動時進行初始化的函數指針放到了該段中,形成一張初始化函數表,在系統啓動過程中會遍歷該表,並調用表中的函數,達到自動初始化的目的。
進入任意一個宏定義,可以查看源碼中的宏定義如下:

/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")

/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn)            INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn)             INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")

繼續展開INIT_EXPORT(fn, level) 如下:

        #define INIT_EXPORT(fn, level)                                                       \
            RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn

其中##代表連接符,把__rt_init_與fn這個形參名字鏈接起來,即__rt_init_fn
查看init_fn_t的定義如下:

typedef int (*init_fn_t)(void);

表示用typedef 定義了一個函數指針類型,init_fn_t _rt_init##fn SECTION(".rti_fn." level) = fn表示定義了一個函數指針變量,並初始化,把fn這個函數的地址賦給__rt_init_fn這個函數指針。
其中SECTION(".rti_fn." level)展開如下:

#define SECTION(x)                  __attribute__((section(x)))

_attribute_((section(“name”))) :將作用的函數或數據放入指定名爲"name"的輸入段中。(在不同的編譯器中實現的方式也有所不同。)
總結:作用就是將函數 fn 的地址賦給一個 __rt_init_fn 的指針,然後放入相應 level 的數據段中。所以函數使用自動初始化宏導出後,這些數據段中就會存儲指向各個初始化函數的指針。

3、自動初始化機制過程

在程序啓動後,會分別運行 rt_components_board_init() 與 rt_components_init() 函數,其中 rt_components_board_init()完成了第1段的初始化,rt_components_init()完成了2到6段的初始化。
1、第一個函數 rt_components_board_init() 的實現:

void rt_components_board_init(void)
{
    const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
}

2、第二個函數 rt_components_board_init() 的實現:

void rt_components_init(void)
{
    const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
    {
        (*fn_ptr)();
    }
}

其中__rt_init_rti_board_start,__rt_init_rti_board_end,__rt_init_rti_end表示不同的區間段。
在系統中,定義了這幾個空函數:rti_start、rti_board_start、rti_board_end、rti_end。劃分成不同的段:0、 0.end 、 1.end 、6.end。
自定義的初始化函數就夾裹在這些段內。

static int rti_start(void)
{
    return 0;
}
INIT_EXPORT(rti_start, "0");

static int rti_board_start(void)
{
    return 0;
}
INIT_EXPORT(rti_board_start, "0.end");

static int rti_board_end(void)
{
    return 0;
}
INIT_EXPORT(rti_board_end, "1.end");

static int rti_end(void)
{
    return 0;
}
INIT_EXPORT(rti_end, "6.end");

這幾個函數的導出,加上上面 6 個初始化宏的導出,就有了這樣一個表格:
在這裏插入圖片描述
查看map文件可知:
在這裏插入圖片描述
其中__rt_init_finsh_system_init 爲內核shell的初始化:

int finsh_system_init(void)
{
    rt_err_t result = RT_EOK;
    rt_thread_t tid;

#ifdef FINSH_USING_SYMTAB
#if defined(__CC_ARM) || defined(__CLANG_ARM)          /* ARM C Compiler */
    extern const int FSymTab$$Base;
    extern const int FSymTab$$Limit;
    extern const int VSymTab$$Base;
    extern const int VSymTab$$Limit;
    finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);
#ifndef FINSH_USING_MSH_ONLY
    finsh_system_var_init(&VSymTab$$Base, &VSymTab$$Limit);
#endif
#elif defined (__ICCARM__) || defined(__ICCRX__)      /* for IAR Compiler */
    finsh_system_function_init(__section_begin("FSymTab"),
                               __section_end("FSymTab"));
    finsh_system_var_init(__section_begin("VSymTab"),
                          __section_end("VSymTab"));
#elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__)
    /* GNU GCC Compiler and TI CCS */
    extern const int __fsymtab_start;
    extern const int __fsymtab_end;
    extern const int __vsymtab_start;
    extern const int __vsymtab_end;
    finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
    finsh_system_var_init(&__vsymtab_start, &__vsymtab_end);
#elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
    finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
    finsh_system_var_init(&__vsymtab_start, &__vsymtab_end);
#elif defined(_MSC_VER)
    unsigned int *ptr_begin, *ptr_end;
		
    if(shell)
    {
        rt_kprintf("finsh shell already init.\n");
        return RT_EOK;
    }

    ptr_begin = (unsigned int *)&__fsym_begin;
    ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int));
    while (*ptr_begin == 0) ptr_begin ++;

    ptr_end = (unsigned int *) &__fsym_end;
    ptr_end --;
    while (*ptr_end == 0) ptr_end --;

    finsh_system_function_init(ptr_begin, ptr_end);
#endif
#endif

#ifdef RT_USING_HEAP
    /* create or set shell structure */
    shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell));
    if (shell == RT_NULL)
    {
        rt_kprintf("no memory for shell\n");
        return -1;
    }
    tid = rt_thread_create(FINSH_THREAD_NAME,
                           finsh_thread_entry, RT_NULL,
                           FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10);
#else
    shell = &_shell;
    tid = &finsh_thread;
    result = rt_thread_init(&finsh_thread,
                            FINSH_THREAD_NAME,
                            finsh_thread_entry, RT_NULL,
                            &finsh_thread_stack[0], sizeof(finsh_thread_stack),
                            FINSH_THREAD_PRIORITY, 10);
#endif /* RT_USING_HEAP */

    rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
    finsh_set_prompt_mode(1);

    if (tid != NULL && result == RT_EOK)
        rt_thread_startup(tid);
    return 0;
}
INIT_APP_EXPORT(finsh_system_init);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章