基于Linux 2.6.32
include\asm-generic\Vmlinux.lds.h中有如下定义:
这等于是安排了名称如.initcall<level>[s].init的一系列section的顺序,level越小,越靠前。
符号__initcall_start记录了这片区域的开始,__initcall_end符号记录了这片区域的结束。
__early_initcall_end则将这片区域更进一步分成了两段。
- #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)
- #define INIT_CALLS \
- VMLINUX_SYMBOL(__initcall_start) = .; \
- INITCALLS \
- VMLINUX_SYMBOL(__initcall_end) = .;
下面来看看,这些section里面究竟是什么内容。
以pci_driver_init函数为例(位于drivers/pci/pci-driver.c)。
- static int __init pci_driver_init(void)
- {
- return bus_register(&pci_bus_type);
- }
- postcore_initcall(pci_driver_init);
可见函数下面跟了一个postcore_initcall宏的调用,追踪宏的定义,得到如下代码。
- #define postcore_initcall(fn) __define_initcall("2",fn,2)
- #define __define_initcall(level,fn,id) \
- static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" level ".init"))) = fn
这样的话,postcore_initcall(pci_driver_init);展开后,其实就是如下内容
- static initcall_t __initcall_pci_driver_init2 __attribute__((__used__)) \
- __attribute__((__section__(".initcall2.init"))) = pci_driver_init;
而initcall_t实际上是个函数指针类型,定义如下
typedef int (*initcall_t)(void);
这样的话,上面的宏展开后,其实就是定义了一个变量__initcall_pci_driver_init2,他是一个函数指针,指向pci_driver_init。这个变量被链接到名为.initcall2.init的section中。
当然,内核中,还有如下一些和postcore_initcall类似的宏(位于include/linux/init.h)。
调用这些宏,就会产生相应的函数指针变量,这些变量则被链接到相应的名称如.initcall<level>[s].init的section中去。
- #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)
下面的问题来了,这些函数指针如何被调用呢?接着往下看。
内核初始化时,kernel_init函数(位于init/main.c)会依次执行到如下函数,完成这些初始化函数的调用。
可见,调用顺序是,level越小的section中的函数指针,越先被调用。
- static void __init do_pre_smp_initcalls(void)
- {
- initcall_t *call;
- for (call = __initcall_start; call < __early_initcall_end; call++)
- do_one_initcall(*call);
- }
- 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();
- }
源文档 <http://blog.youkuaiyun.com/crazycoder8848/article/details/50847013>