内核版本号:3.4~4.2
内核arch_initcall的实现
内核有种函数的标示方法,比如:arch_initcall(VSBOARD_XXXX),可以在内核初始化后自动运行VSBOARD_XXXX函数,以此来达到内核模块可裁剪的目的。
他的具体实现原理:
1、使用arch_initcall宏,会在生成的目标ELF文件增加一个名为".initcall"level".init"的section,并将函数指针放置于该section中。
如果多次调用arch_initcall,将会有多个函数指针放置于该section(他的巧妙之处就利用编译器这点)。
2、vmlinux.lds.S(include"vmlinux.lds.h"),会通过INIT_CALLS宏,将.initcallxxx.init系列section重排,并且通过宏,将函数指针变量__initcall0_start指向这些section的位置。
3、在start_kernel的最后阶段,调用该函数指针__initcall0_start:
start_kernel->reset_init->kernel_init->do_basic_setup->
do_initcalls
如何自定义initcall
比如自定义vsboard_initcall宏,并且希望他在内核初始化之初就能自动运行:
1、定义宏:
#definevsboard_initcall(fn)\
static initcall_t __initcall_##fn##vsboard __used\
__attribute__((__section__(".initcallvsboard.init"))) = fn
通过该宏标示的函数,会在ELF文件中增加名为.initcallvsboard.init的section。
2、想要自动初始化的函数用宏标示:
vsboard_initcall(VSBOARD_Test);
3、在vmlinux.lds.h中添加该section的函数指针定义:
#define INIT_CALLS \
VMLINUX_SYMBOL(__vsboard_initcall_start)= .; \
*(.initcallvsboard.init) \
VMLINUX_SYMBOL(__initcall_start) =.; \
INITCALLS \
VMLINUX_SYMBOL(__initcall_end) =.;
4、在内核想运行该section函数指针的地方添加代码
extern initcall_t __vsboard_initcall_start[];
initcall_t *fntest;
int ret;
for (fntest = __vsboard_initcall_start;fntest < __initcall_start; fntest++)
ret = (*fntest)();
注意:fntest的结束位置__initcall_start,和第三点宏中的变量定义相呼应(不同内核版本略有不同)。