initcall 机制。它利用gcc的扩展功能以及ld 的连接控制脚本实现了在代码初始化的过程中通过简
单的循环就实现了相关模块和组件的初始化。核心代码:
do_initcalls函数如下:
void d o_ i n i t c a l l s ( void )
{
i n i t c a l l _t *call ;
f o r ( c a l l = &__ i n i t c a l l _s t a r t ; c a l l < &__ i n i t c a l l _e n d ; c a l l ++) {
(* c a l l ) ( ) ;
}
}
其中__initcall_start和__initcall_end在源码中并无定义,只是在程序文件中
申明为外部变量:
e x t e r n i n i t c a l l _t __i n i t c a l l _s t a r t , __i n i t c a l l _e n d ;
#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn __attribute__((__used__))\
__attribute__((__section__(".initcall" level ".init"))) = fn
#define core_initcall(fn) __define_initcall("1",fn)
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
#define subsys_initcall(fn) __define_initcall("4",fn)
#define comp_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)
这几个变量是在我们的链接脚本lds中定义的,
其含义是指示连接程序让__initcall_start指向代码节.initcall.init的
节首,而__initcall_end指向.initcall.init的节尾。只要把需要初始化调用的函数的指针放在__initcall_start和__initcall_end之
间的节内,函数就会在内核初始化时被调用。
ldscript脚本
/* 以下是7个级别的初始化段 */
. = ALIGN(4);
__initcall1_start = .;
.initcall1_start :{*(.initcall1.init)}
__initcall1_end = .;
. = ALIGN(4);
__initcall2_start = .;
.initcall2_start :{*(.initcall2.init)}
__initcall2_end = .;
. = ALIGN(4);
__initcall3_start = .;
.initcall3_start :{*(.initcall3.init)}
__initcall3_end = .;
. = ALIGN(4);
__initcall4_start = .;
.initcall4_start :{*(.initcall4.init)}
__initcall4_end = .;
. = ALIGN(4);
__initcall5_start = .;
.initcall5_start :{*(.initcall5.init)}
__initcall5_end = .;
. = ALIGN(4);
__initcall6_start = .;
.initcall6_start :{*(.initcall6.init)}
__initcall6_end = .;
. = ALIGN(4);
__initcall7_start = .;
.initcall7_start :{*(.initcall7.init)}
__initcall7_end = .;