自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。
以rtthread的can初始化为例:
int rt_hw_can_init(void)
{
// 初始化can
rt_hw_can_register(&drv_can1.device,
drv_can1.name,
&_can_ops,
&drv_can1);
}
INIT_BOARD_EXPORT(rt_hw_can_init);
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
#define SECTION(x) __attribute__((section(x)))
#define RT_USED __attribute__((used))
按宏一层层展开:
INIT_EXPORT(rt_hw_can_init, "1");
RT_USED const init_fn_t __rt_init_rt_hw_can_init SECTION(".rti_fn." "1") =
rt_hw_can_init;
__attribute__((used)) const init_fn_t __rt_init_rt_hw_can_init
__attribute__((section(".rti_fn." "1"))) =
rt_hw_can_init;
typedef int (*init_fn_t)(void);
__attribute__((used))作用是告诉编译器该语句有用的,需要执行的,没有这个编译可能会优化掉,导致不执行。
init_fn_t为函数指针类型,那么const init_fn_t __rt_init_rt_hw_can_init = rt_hw_can_init;这语句的意思是定义了一个函数指针类型的常量__rt_init_rt_hw_can_init,并初始化值为rt_hw_can_init。这样__rt_init_rt_hw_can_init就是指向rt_hw_can_init的函数指针,只要调用(*__rt_init_rt_hw_can_init)()既可以执行rt_hw_can_init函数。
再看__attribute__((section(".rti_fn." "1"))),这个意思是将__rt_init_rt_hw_can_init放于".rti_fn.1"输入段中,由以下Map文件可看出
这样我们就知道__rt_init_rt_hw_can_init所存放的地址,通过遍历".rti_fn.1"输入段就可执行该输入段中的所有初始化函数,rtthread中源码如下:
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");
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)();
}
// ...
}
rtthread在系统启动过程中会调用到rt_components_board_init函数。INIT_EXPORT(rti_board_start, "0.end"); INIT_EXPORT(rti_board_end, "1.end"); 定义了__rt_init_rti_board_start和__rt_init_rti_board_end这两个函数指针,他们保存的地址刚好是输入段 ".rti_fn.1"前一个以及后一个。所以遍历即可得到".rti_fn.1"中的所有函数指针。通过函数指针就可以调用到can的初始化函数rt_hw_can_init,实现自动初始化,不需要显式调用rt_hw_can_init。
所以想要被自动初始化,只需要通过INIT_BOARD_EXPORT这个宏声明即可。