1. 自动初始化API
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* 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")
初始化顺序 | API | 描述 |
---|---|---|
1 | INIT_BOARD_EXPORT(fn) | 非常早期的初始化,此时调度器还未启动 |
2 | INIT_PREV_EXPORT(fn) | 主要是用于纯软件的初始化、没有太多依赖的函数 |
3 | INIT_DEVICE_EXPORT(fn) | 外设驱动初始化相关,比如网卡设备 |
4 | INIT_COMPONENT_EXPORT(fn) | 组件初始化,比如文件系统或者 LWIP |
5 | INIT_ENV_EXPORT(fn) | 系统环境初始化,比如挂载文件系统 |
6 | INIT_APP_EXPORT(fn) | 应用初始化,比如 GUI 应用 |
2. 原理分析
2.1 INIT_EXPORT函数
各个初始化函数可以看出最终调用的都是INIT_EXPORT函数,只是传参不同
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
/*
1. fn表示需要初始化的函数,传参为函数指针
2. level表示将函数指针放到哪一个段
*/
2.2 预备知识
-
RT_USED
#define RT_USED __attribute__((used)) /* 标记为attribute__((used))的函数被标记在目标文件中,以避免链接器删除未使用的节。*/
-
init_fn_t 类型
typedef int (*init_fn_t)(void); /* 定义了一个返回值为int,函数参数为void的一个函数指针类型并重命名为init_fn_t。 */
-
SECTION
#define SECTION(x) __attribute__((section(x))) /* 将作用的函数或数据放入指定名为name的输入段中 */
-
将宏展开
RT_USED const init_fn_t __rt_init_fn SECTION(".rti_fn." level) = fn
函数 fn 的指针赋值给__rt_init_fn这个变量,这个变量的类型是RT_USED const init_fn_t,这个变量存放在指定的段.rti_fn.level中。所以函数使用自动初始化宏导出后,这些数据段中就会存储指向各个初始化函数的指针。当我们对这些指针进行解引用的时候也就相当于执行了相应的函数。
2.3 段的划分
在 component.c中对各个段进行了划分,源码如下
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");
上面使用 INIT_EXPORT
宏导出的段分布如下表所示
序号 | 段名 | 函数指针/函数名 |
---|---|---|
1 | .rti_fn.0 | __rt_init_rti_start |
2 | .rti_fn.0.end | __rti_init_rti_board_start |
3 | .rti_fn.1.end | __rti_init_rti_board_end |
4 | .rti_fn.6.end | __rti_init_rti_end |
__rti_init_rti_end
序号 | 段名 | 函数指针/函数名 |
---|---|---|
1 | .rti_fn.0 | __rt_init_rti_start |
2 | .rti_fn.0.end | __rti_init_rti_board_start |
.rti_fn.1 | INIT_BOARD_EXPORT(fn) | |
4 | .rti_fn.1.end | __rti_init_rti_board_end |
5 | .rti_fn.2 | INIT_PREV_EXPORT(fn) |
6 | .rti_fn.3 | INIT_DEVICE_EXPORT(fn) |
7 | .rti_fn.4 | INIT_COMPONENT_EXPORT(fn) |
8 | .rti_fn.5 | INIT_ENV_EXPORT(fn) |
9 | .rti_fn.6 | INIT_APP_EXPORT(fn) |
10 | .rti_fn.6.end | __rti_init_rti_end |
2.4 rt_components_board_init 函数
void rt_components_board_init(void)
{
/* …………………………………… */
volatile const init_fn_t *fn_ptr;s
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
}
这段代码定义了一个 fn_ptr 指针,当指针的范围在 __rt_init_rti_board_start 和 __rt_init_rti_board_end 范围之内时就对该指针进行解引用,这里的指针就是自动初始化时放的函数指针,所以这里就相当与函数的执行。也就是执行了 INIT_BOARD_EXPORT(fn) 导出的函数
2.5 rt_components_init 函数
void rt_components_board_init(void)
{
/* …………………………………… */
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr++)
{
(*fn_ptr)();
}
}
定义了一个 fn_ptr 指针,当指针的范围在 __rt_init_rti_board_end
和__rt_init_rti_end
范围之内时就对该指针进行解引用,这里的指针就是自动初始化时放的函数指针,所以这里就相当与函数的执行。也就是执行了INIT_PREV_EXPORT(fn)
到 INIT_APP_EXPORT(fn)
这两个段之间导出的函数
- rt_components_board_init()完成了第 1 段,也就是初始化了由INIT_BOARD_EXPORT(fn)的初始化的所有函数,也就是__rt_init_rti_board_start到 __rt_init_rti_board_end之间的函数指针。
- rt_components_init()完成了第2 到第6 段,也就是按顺序初始化了由INIT_PREV_EXPORT(fn)到INIT_DEVICE_EXPORT(fn)到INIT_COMPONENT_EXPORT(fn)、INIT_ENV_EXPORT(fn)、INIT_APP_EXPORT(fn)初始化的所有函数,也就是从__rt_init_rti_board_end到__rt_init_rti_end之间的函数指针。
- 当使用自动初始化导出宏去初始化一个函数时,是由系统中的这两个函数进行遍历函数指针执行的。