RT-thread組件初始化代碼分析

        RT-thread提供了組件化功能,具體實現是在components/init文件夾下components.c文件中實現的。應用組件化功能首先在rtconfig.h中添加宏定義#define RT_USING_COMPONENTS_INIT;若須要啓用調試模式,則還要添加#define RT_DEBUG_INIT 1html

void rt_components_board_init(void)
{
#ifndef _MSC_VER #if RT_DEBUG_INIT                      //啓用初始化調試模式,主要目的爲將各個組件初始化的狀態經過串口打印到PC端(在rtconfig.h中宏定義爲1)
    int result;
    const struct rt_init_desc *desc;   //rt_init_desc是在rtdef.h中定義的結構體類型
    for (desc = &__rt_init_desc_rti_start; desc < &__rt_init_desc_rti_board_end; desc ++)
    {
        rt_kprintf("initialize %s", desc->fn_name);
        result = desc->fn();
        rt_kprintf(":%d done\n", result);
    }
#else
    const init_fn_t *fn_ptr; for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++) { (*fn_ptr)(); }
#endif
#endif
}
void rt_components_init(void)
{
#ifndef _MSC_VER #if RT_DEBUG_INIT                    //啓用初始化調試模式
    int result;
    const struct rt_init_desc *desc; //rt_init_desc爲在rtdef.h中定義的結構體類型,這裏定義了指向該結構體類型的指針變量

    rt_kprintf("do components intialization.\n");
    for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++) //注意這裏的 & 符號
    {
        rt_kprintf("initialize %s", desc->fn_name);
        result = desc->fn();
        rt_kprintf(":%d done\n", result);
    }
#else
    const init_fn_t *fn_ptr; //定義指向該函數類型的指針,函數指針變量fn_ptr指向的是初始化函數首地址,fn_ptr自己並不表明初始化函數首地址 for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++) //注意這裏的 & 符號 { (*fn_ptr)(); //從函數指針中取出初始化函數首地址(函數名)並進行初始化 }
#endif
#else #ifdef RT_USING_MODULE
    rt_system_module_init();
#endif #ifdef RT_USING_FINSH
    /* initialize finsh */
    finsh_system_init();
    finsh_set_device(RT_CONSOLE_DEVICE_NAME);
#endif #ifdef RT_USING_LWIP
    /* initialize lwip stack */
    /* register ethernetif device */
    eth_system_device_init();

    /* initialize lwip system */
    lwip_system_init();
    rt_kprintf("TCP/IP initialized!\n");
#endif #ifdef RT_USING_DFS
    /* initialize the device file system */
    dfs_init();

#ifdef RT_USING_DFS_ELMFAT
    /* initialize the elm chan FatFS file system*/
    elm_init();
#endif

#if defined(RT_USING_DFS_NFS) && defined(RT_USING_LWIP)
    /* initialize NFSv3 client file system */
    nfs_init();
#endif #ifdef RT_USING_DFS_YAFFS2
    dfs_yaffs2_init();
#endif #ifdef RT_USING_DFS_UFFS
    dfs_uffs_init();
#endif #ifdef RT_USING_DFS_JFFS2
    dfs_jffs2_init();
#endif #ifdef RT_USING_DFS_ROMFS
    dfs_romfs_init();
#endif #ifdef RT_USING_DFS_RAMFS
    dfs_ramfs_init();
#endif #ifdef RT_USING_DFS_DEVFS
    devfs_init();
#endif
#endif /* end of RT_USING_DFS */ #ifdef RT_USING_NEWLIB
    libc_system_init(RT_CONSOLE_DEVICE_NAME);
#else
    /* the pthread system initialization will be initiallized in libc */ #ifdef RT_USING_PTHREADS 
    pthread_system_init();
#endif
#endif #ifdef RT_USING_RTGUI
    rtgui_system_server_init();
#endif #ifdef RT_USING_USB_HOST
    rt_usb_host_init();
#endif
#endif
}

 上面代碼紅色粗體是組件初始化的入口,是一個函數指針。init_fn_t 的定義在rtdef.h中,以下所示:shell

/* initialization export */ #ifdef RT_USING_COMPONENTS_INIT //在rtconfig.h中進行宏定義,則啓用RT-thread的組件初始化功能
typedef int (*init_fn_t)(void); //對指向int ()(void)函數類型的指針類型取別名init_fn_t。 利用這個別名可定義指向該函數類型的指針,也可用於直接定義該類型的函數名
#ifdef _MSC_VER /* we do not support MS VC++ compiler *///這裏沒有采用microsoft VC++ complier
    #define INIT_EXPORT(fn, level)
#else
    #if RT_DEBUG_INIT           //啓用初始化調試模式
        struct rt_init_desc
        {
            const char* fn_name;
            const init_fn_t fn;                                         //這裏定義的fn並非函數指針,而是直接定義函數名
        };
        #define INIT_EXPORT(fn, level)          \
            const char __rti_##fn##_name[] = #fn; \         //C語言中#鏈接符是把傳遞過來的參數當成字符串進行替代
            const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = \   //定義結構體變量__rt_init_desc_##fn
            { __rti_##fn##_name, fn};
    #else
        #define INIT_EXPORT(fn, level) \ const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn //這裏定義的__rt_init_##fn並非函數指針,而是直接定義函數名
    #endif
#endif
#else
#define INIT_EXPORT(fn, level)
#endif

/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")
/* device/component/fs/app init routines will be called in init_thread */
/* device initialization */
#define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "2")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "3")
/* file system initialization (dfs-elm, dfs-rom, ...) */
#define INIT_FS_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")

其中typdef int (*init_fn_t)(void)的意思是定義init_fn_t爲指向函數的指針類型,該函數返回int類型值。這樣一來,咱們對(*init_fn_t)()的意思就清楚了。app

INIT_EXPOT(fn,level) 的表達式是const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn,其中##連詞符。jsp

      ## 鏈接符號由兩個井號組成,其功能是在帶參數的宏定義中將兩個子串(token)聯接起來,從而造成一個新的子串。但它不可以是第一個或者最後一個子串。所謂的子串(token)就是指編譯器可以識別的最小語法單元。具體的定義在編譯原理裏有詳盡的解釋,但不知道也無所謂。同時值得注意的是#鏈接符是把傳遞過來的參數當成字符串進行替代。函數

SECTION的定義爲:工具

      #define SECTION(x)   __attribute__((section(x)))組件化

RealView 編譯工具 編譯器參考指南中給出了下面的解釋:測試

   __attribute__((section("name")))ui

      一般,ARM 編譯器將它生成的對象放在節中,如 data 和 bss。可是,您可能須要使用其餘數據節,或者但願變量出如今特殊節中,例如,便於映射到特殊硬件。section 屬性指定變量必須放在特定數據節中。若是使用 section 屬性,則將只讀變量放在 RO 數據節中,而將讀寫變量放在 RW 數據節中,除非您使用 zero_init 屬性。在這種狀況下,變量被放在 ZI 節中。到此,意思已經很明瞭了,編譯器能夠根據對section("name")中的name指定,能夠將它生成的數據放到特定的數據節中。spa

相似的這樣的方式,Linux也提供了一些借鑑,把一個函數的地址(注意是函數地址,而不是函數自己)輸出到一個獨立的section中,同時按照必定順序進行排列,例如:
.rti_fn.0
.rti_fn.1
.rti_fn.2
...
.rti_fn.7
這樣幾個section(這樣幾個不一樣的section也給出了排列的順序)。同時把.rti_fn.0和.rti_fn.7保留給系統使用,分別定義出兩個樁放置在這兩個點上。

也能夠按照RT-Thread的形式定義簡化的宏:
typedef int (*init_fn_t)(void);
#define INIT_EXPORT(fn, level)        \
        const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

#define INIT_BOARD_EXPORT(fn)                INIT_EXPORT(fn, "1")
#define INIT_CPU_EXPORT(fn)                     INIT_EXPORT(fn, "2")
#define INIT_DEVICE_EXPORT(fn)                INIT_EXPORT(fn, "3")
#define INIT_COMPONENT_EXPORT(fn)         INIT_EXPORT(fn, "4")
#define INIT_FS_EXPORT(fn)                       INIT_EXPORT(fn, "5")
#define INIT_APP_EXPORT(fn)                     INIT_EXPORT(fn, "6")
INIT_EXPORT宏用於輸出一個函數到初始化序列中,相應的能夠定義一些更簡化的宏。
這樣兩個樁能夠定義成:
static int rti_start(void)
{
        return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_end(void)
{
        return 0;
}
INIT_EXPORT(rti_end,"7");
根據這兩個樁的位置,簡化的rt_components_init()函數就能夠變成:
void rt_components_init(void)
{
        const init_fn_t* fn_ptr;
        for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_end; )
        {
                (*fn_ptr)();
                fn_ptr ++;
        }
}

事實上,aozima作了工程測試獲得了驗證。工程編譯後,從map文件找到相關部份內容:

InitFuncSym$$Base                   0x00000e18   Number    0init_1.o(InitFuncSym)

__rt_init_init_1                         0x00000e18   Data         4init_1.o(InitFuncSym)

__rt_init_init_2                         0x00000e1c   Data         4init_2.o(InitFuncSym)

__rt_init_init_3                         0x00000e20   Data         4init_3.o(InitFuncSym)

__rt_init_init_4                         0x00000e24   Data         4init_4.o(InitFuncSym)

__rt_init_init_5                         0x00000e28   Data         4init_5.o(InitFuncSym)

__rt_init_init_6                         0x00000e2c   Data         4init_6.o(InitFuncSym)

InitFuncSym$$Limit                  0x00000e30   Number    0init_6.o(InitFuncSym)

儘管數據存放在不一樣的文件,但從這裏這能夠看到是空間連續分配的

總之,經過定義

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

能夠系統各部分的組件經過INIT_EXPORT(fn,level)放到一個特定代碼段當中,簡言之,當咱們要初始化某個組件時,定義完這個初始化函數後,根據上面宏定義的註釋,在其下面接着放一條INIT_XXX_EXPORT(fn)就能夠了。至關於一個指定到特定代碼段的隱形調用,並且要清楚這個段中是不一樣組件初始化函數的入口地址,例如:

int my_init_fun(void) {... ...}

INIT_XXX_EXPORT(my_init_fun)

例如在finsh組件shell.c中:

int finsh_system_init(void) {......}

INIT_COMPONENT_EXPORT(finsh_system_init);

注意:定義的初始化函數必須知足輸入參數類型爲void,返回類型爲int,即爲typedef int (*init_fn_t)(void)中定義的函數類型。

相關文章
相關標籤/搜索