其他分享
首页 > 其他分享> > RTT笔记-分析自动初始化机制转

RTT笔记-分析自动初始化机制转

作者:互联网

首先全局搜索一个任意的自启动宏,便能找到在rtdef.h中由如下定义

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

关于宏INIT_EXPORT的定义就就在上方

 1 #ifdef RT_USING_COMPONENTS_INIT 
 2 typedef int (*init_fn_t)(void);
 3 #ifdef _MSC_VER /* we do not support MS VC++ compiler */
 4     #define INIT_EXPORT(fn, level)
 5 #else
 6     #if RT_DEBUG_INIT
 7         struct rt_init_desc
 8         {
 9             const char* fn_name;
10             const init_fn_t fn;
11         };
12         #define INIT_EXPORT(fn, level)                                                       \
13             const char __rti_##fn##_name[] = #fn;                                            \
14             RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = \
15             { __rti_##fn##_name, fn};
16     #else
17         #define INIT_EXPORT(fn, level)                                                       \
18             RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
19     #endif
20 #endif
21 #else
22 #define INIT_EXPORT(fn, level)
23 #endif
针对上面代码,逐句分析下。
首先RT_USING_COMPONENTS_INIT宏需要在config.h中定义,否则自启动是无效的。
然后使用typedef定义了一个函数指针类型
这里补充一下关于typedef:
目前我知道的typedef有两种用法,其一是起别名,其二是定义新的类型,举个例程说明这两种用法
 1 //生产了新类型fun_p
 2 typedef int (*fun_p)(void);
 3 int app(void)
 4 {
 5     return 0;
 6 }
 7 
 8 typedef struct sTest
 9 {
10     fun_p * app_p;
11 }Test_s; 
12 
13 Test_s test;
14 tset.app_p = app;

回到上文,由于#ifdef后的宏均是未定义,所以一路走else,那么就仅仅剩一句话

 1 RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn 


首先看看RT_USED这个宏,通用定义也在rtdeh.h中  1 #define RT_USED __attribute__((used))  __attribute __((section(“name”)))

该函数便是实现自动初始化的关键了,他的作用是将标记的数据或者函数在编译时放到name的数据段中去。
例如系统中有如下语句  1 components.c(60) : INIT_EXPORT(rti_start, "0");    在编译后生成的map文件中能够找到对应信息,名叫__rt_init_rti_start 的指针被保存在了.rti_fn.0字段中去 __rt_init_rti_start 0x0801e6b8 Data 4 components.o(.rti_fn.0)  

综上那么完整语句的翻译便是: 定义了一个名为(_rt_init+需要自动启的函数名)的函数指针,将其保存在(.rti_fn.level)数据段中,并且及时不使用也不会被编译器优化。

到这里基本就能明白自启动的方式了。也就是逐个建立一个指针指向需要自启动的函数,然后将这些指针保存到特定的数据段中。main启动时候,只需要将数据段中的指针函数全部执行一遍即可。

接下来我们看执行初始化的地方,也就是在components.c中
一上来便定义了一些标杆,用来区间化之前准备往里塞的函数指针

 1 static int rti_start(void)
 2 {
 3     return 0;
 4 }
 5 INIT_EXPORT(rti_start, "0");
 6 
 7 static int rti_board_start(void)
 8 {
 9     return 0;
10 }
11 INIT_EXPORT(rti_board_start, "0.end");
12 
13 static int rti_board_end(void)
14 {
15     return 0;
16 }
17 INIT_EXPORT(rti_board_end, "1.end");
18 
19 static int rti_end(void)
20 {
21     return 0;
22 }
23 INIT_EXPORT(rti_end, "6.end");

我们再看看map中的情况

 1     __rt_init_rti_start                      0x0801e6dc   Data           4  components.o(.rti_fn.0)
 2     __rt_init_rti_board_start                0x0801e6e0   Data           4  components.o(.rti_fn.0.end)
 3     __rt_init_rt_hw_spi_init                 0x0801e6e4   Data           4  drv_spi.o(.rti_fn.1)
 4     __rt_init_rti_board_end                  0x0801e6e8   Data           4  components.o(.rti_fn.1.end)
 5     __rt_init_ulog_init                      0x0801e6ec   Data           4  ulog.o(.rti_fn.2)
 6     __rt_init_ulog_console_backend_init      0x0801e6f0   Data           4  console_be.o(.rti_fn.2)
 7     __rt_init_finsh_system_init              0x0801e6f4   Data           4  shell.o(.rti_fn.6)
 8     __rt_init_fal_init                       0x0801e6f8   Data           4  fal.o(.rti_fn.6)
 9     __rt_init_rti_end                        0x0801e6fc   Data           4  components.o(.rti_fn.6.end)
10    

我们想自启动的函数__rt_init_rt_hw_spi_init、__rt_init_ulog_init 等都被包夹在了这些标识杆中间。至于他的排序问题,文末将用代码进行测试推论。

按照系统执行顺序来分别看下自启动的两个函数:
首先是 rtthread_startup() →void rt_hw_board_init() →void rt_components_board_init(void)

按照系统执行顺序来分别看下自启动的两个函数:
首先是 rtthread_startup() →void rt_hw_board_init() →void rt_components_board_init(void)
1     const init_fn_t *fn_ptr;
2 
3     for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
4     {
5         (*fn_ptr)();
6     }

其中__rt_init_rti_board_start和__rt_init_rti_board_end便是上面的两个标志杆,是经过宏里面的##拼接后的结果,然后我们再看看上面的map,就发现这个for循环实际上是执行了被包夹的__rt_init_rti_board_start和__rt_init_rt_hw_spi_init,拆解一下就是函数rti_board_start和rt_hw_spi_init。

我们再看第二个自启动的函数
rtthread_startup() →rt_application_init()→void main_thread_entry(void *parameter)→rt_components_init();

1     const init_fn_t *fn_ptr;
2 
3     for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
4     {
5         (*fn_ptr)();
6     }

这里和上面类似,只是标志杆变为了level2~6之间的函数了。也就是level02间的函数是一起执行的,level2~6间的函数是一起执行的。

下来我们研究一下这个字段的排序问题
首先由已知,在.rti_fn.后面是以数由小到大排序。

 那么尝试一下在后面添加字符,添加两个新的标志杆

 字符排在了数字后面,然后再添加一个大写字母

  A排序到了小写字母之前数字之后,也就是这个排序可能就是ascii码的排序了。 

还有个问题就是同字段的两个函数指针的顺序如何呢,例如

1     __rt_init_ulog_init                      0x0801e6f8   Data           4  ulog.o(.rti_fn.2)
2     __rt_init_ulog_console_backend_init      0x0801e6fc   Data           4  console_be.o(.rti_fn.2)

我将之前的标杆修改为

 1 //测试用标志杆
 2 static int rti_A(void)
 3 {
 4    return 0;
 5 }
 6 INIT_EXPORT(rti_A, "2");
 7 
 8 //测试用标志杆
 9 static int rti_a(void)
10 {
11    return 0;
12 }
13 INIT_EXPORT(rti_a, "2");
14 
15 static int rti_1(void)
16 {
17    return 0;
18 }
19 INIT_EXPORT(rti_1, "2");

然后map结果是:

 1     __rt_init_rti_start                      0x0801e6e8   Data           4  components.o(.rti_fn.0)
 2     __rt_init_rti_board_start                0x0801e6ec   Data           4  components.o(.rti_fn.0.end)
 3     __rt_init_rt_hw_spi_init                 0x0801e6f0   Data           4  drv_spi.o(.rti_fn.1)
 4     __rt_init_rti_board_end                  0x0801e6f4   Data           4  components.o(.rti_fn.1.end)
 5     __rt_init_rti_A                          0x0801e6f8   Data           4  components.o(.rti_fn.2)
 6     __rt_init_rti_a                          0x0801e6fc   Data           4  components.o(.rti_fn.2)
 7     __rt_init_rti_1                          0x0801e700   Data           4  components.o(.rti_fn.2)
 8     __rt_init_ulog_init                      0x0801e704   Data           4  ulog.o(.rti_fn.2)
 9     __rt_init_ulog_console_backend_init      0x0801e708   Data           4  console_be.o(.rti_fn.2)
10     __rt_init_finsh_system_init              0x0801e70c   Data           4  shell.o(.rti_fn.6)
11     __rt_init_fal_init                       0x0801e710   Data           4  fal.o(.rti_fn.6)
12     __rt_init_rti_end                        0x0801e714   Data           4  components.o(.rti_fn.6.end)
13   

可以看出排序和我代码顺序有关,也就是应该和编译顺序有关。

原文链接:https://www.jianshu.com/p/9d377ddc8acc

标签:rt,初始化,rti,INIT,笔记,RTT,init,__,fn
来源: https://www.cnblogs.com/TonyJia/p/13518598.html