博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RT-thread组件初始化代码分析
阅读量:6497 次
发布时间:2019-06-24

本文共 7628 字,大约阅读时间需要 25 分钟。

        RT-thread提供了组件化功能,具体实现是在components/init文件夹下components.c文件中实现的。应用组件化功能首先在rtconfig.h中添加宏定义#define RT_USING_COMPONENTS_INIT;若需要启用调试模式,则还要添加#define RT_DEBUG_INIT 1

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中,如下所示:

/* 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)()的意思就清楚了。

INIT_EXPOT(fn,level) 的表达式是const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn,其中##连词符。

      ## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#连接符是把传递过来的参数当成字符串进行替代。

SECTION的定义为:

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

中给出了下面的解释:

   __attribute__((section("name")))

      通常,ARM 编译器将它生成的对象放在节中,如 data 和 bss。但是,您可能需要使用其他数据节,或者希望变量出现在特殊节中,例如,便于映射到特殊硬件。section 属性指定变量必须放在特定数据节中。如果使用 section 属性,则将只读变量放在 RO 数据节中,而将读写变量放在 RW 数据节中,除非您使用 zero_init 属性。在这种情况下,变量被放在 ZI 节中。到此,意思已经很明了了,编译器可以根据对section("name")中的name指定,可以将它生成的数据放到特定的数据节中。

类似的这样的方式,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)中定义的函数类型。

转载于:https://www.cnblogs.com/King-Gentleman/p/4570559.html

你可能感兴趣的文章
最长回文子序列-----动态规划
查看>>
Vue国际化实现
查看>>
设计模式:单例模式
查看>>
FLASH位宽为8、16、32时,CPU与外设之间地址线的连接方法
查看>>
双网卡一般情况不能有两个网关 (转)
查看>>
xshell 远程连接Linux
查看>>
Linux计划任务及压缩归档(week2_day1)--技术流ken
查看>>
微信小程序登录 该死的官方文档TypeError: the JSON object must be str, not 'bytes'
查看>>
VMware 虚拟机克隆 CentOS 6.5 之后,网络配置问题的解决方案
查看>>
Python ( 1 ) ----- 简介
查看>>
[linux基础学习]run level
查看>>
第七周学习总结
查看>>
一步步的教你安装UChome (UChome 安装教程)
查看>>
[DeeplearningAI笔记]序列模型1.5-1.6不同类型的循环神经网络/语言模型与序列生成...
查看>>
P2533 [AHOI2012]信号塔
查看>>
Android电话拨号器(uri格式)与四种设置点击事件的方法
查看>>
java web中对json的使用
查看>>
TYVJ P1051 选课 Label:多叉转二叉&&树形dp(虐心♥)
查看>>
将数据库中提取出来的数据在后台进行分页处理
查看>>
bzoj1034
查看>>