在模块加载的过程中,init头文件非常重要,它定义了module_init和xxx_initcall以及相应的clearup函数,还决定了模块的加载顺序级别以及模块编译进内核和动态加载时module_init所做的不同的事情。
而vmlinux.lds文件用来决定初始化所用的内存的分布,我们看看内核初始化的内存分布:
.init.text : {
_sinittext = .;
*(.init.text)
_einittext = .;
}
.init.data : {
*(.init.data);
__vtop_table_begin = .;
*(.vtop_fixup);
__vtop_table_end = .;
__ptov_table_begin = .;
*(.ptov_fixup);
__ptov_table_end = .;
}
. = ALIGN(16);
__setup_start = .;
.init.setup : { *(.init.setup) }
__setup_end = .;
__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
内存的分布可以用下图来说明:
其中__init用来标示的是初始化函数,在初始化后不会再调用,__initdata是初始化数据, __initparam是初始化参数,其他7个初始化宏就是初始化函数会用到的。所有标示为__init的函数都会在链接的时候都会放在.init.text中,放得顺序是不确定的,与编译、链接顺序有关,同时所有__init函数都会在.initcallx.init中放一个函数指针,初始化的时候按照.initcall1.init->.initcall7.init的顺序初始化,.initcallx.init有不同的别名,在init.h中定义:
#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn
#define early_initcall(fn) __define_initcall(".early1",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 fs_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)
这些定义就说明了我们在使用过程中的宏定义以及他们的加载顺序,但是,我们一般使用的都是module_init()函数,那么它是怎么定义的呢:
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
可以看出,通常用的module__init()函数都使用了devince_initcall定义,即第6个加载顺序的initcall宏。
do_initcalls函数
那么这些初始化函数在什么地方执行呢?在main.c的do_initcalls函数里面执行:
static void __init do_initcalls(void)
{
initcall_t *call;
int count = preempt_count();
for (call = &__initcall_start; call < &__initcall_end; call++) {
char *msg;
if (initcall_debug) {
printk(KERN_DEBUG "Calling initcall 0x%p", *call);
print_fn_descriptor_symbol(": %s()", (unsigned long) *call);
printk("\n");
}
(*call)();
…………
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
这个函数从__initcall_start开始到__initcall_end依次执行标示为__init的函数,从而完成初始化