分类: LINUX
2017-11-09 18:18:19
原文地址:module_init的内核调用顺序 作者:_ChinaUnix
现在以module_init为例分析initcall在内核中的调用顺序
在头文件init.h中,有如下定义:
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
继续看:
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
最终我们看到的是module_init的真身:__define_initcall(level,fn,id),仔细推敲这个真身,知道这是个宏,它把传给module_init的函数名组装成以__initcall为前缀的、以6为后缀的函数名,并把这个函数定义到代码段.initcall6.init里面。
在代码段.initcall6.init里面?这函数躲在这里干嘛,啥时候才轮得到它出头啊!找到有此字符串的文件vmlinux.lds.h,相关代码如下所示:
#define INITCALLS \
*(.initcallearly.init) \
VMLINUX_SYMBOL(__early_initcall_end) = .; \
*(.initcall0.init) \
*(.initcall0s.init) \
*(.initcall1.init) \
*(.initcall1s.init) \
*(.initcall2.init) \
*(.initcall2s.init) \
*(.initcall3.init) \
*(.initcall3s.init) \
*(.initcall4.init) \
*(.initcall4s.init) \
*(.initcall5.init) \
*(.initcall5s.init) \
*(.initcallrootfs.init) \
*(.initcall6.init) \
*(.initcall6s.init) \
*(.initcall7.init) \
*(.initcall7s.init)
在vmlinux.lds.S里面
__initcall_start = .;
INITCALLS
__initcall_end = .;
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
__security_initcall_start = .;
*(.security_initcall.init)
__security_initcall_end = .;
言归正传,INITCALLS在__initcall_start = .与__initcall_end = .之间,表示INITCALLS宏内涵的相关段代码顺序存放在这里,module_init所代表段的镶嵌其中
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
至此我们应该明白了各个initcall是如何来的以及在代码存储空间上是怎么组织的了吧,下面来看看内核是什么时候调用它的。
内核启动流程如下所示
Main()àlinux_main()àstart_uml()àstart_kernel_proc()àstart_kernelàrest_inità kernel_inità do_basic_setupà do_initcalls
do_initcalls的代码如下所示
static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
上面的代码中,__early_initcall_end在INITCALLS内定义,__initcall_end在文件vmlinux.lds.S中定义,他们代表的是一些初始化函数的指针数组起始与结束地址,执行函数do_initcalls时,包含在这各指针数组里面的函数顺序的被调用以执行一些必要的初始化工作。至此,明白的各initcall的执行时刻了吧。