主要分析do_basic_setup函数里面的do_initcalls()函数,这个函数用来调用全部编译内核的驱动模块中的初始化函数。linux
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 与 __initcall_end之间有ide
#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)
do_initcalls函数完成对*initcall0-7*的函数的调用工做,遍历全部的模块,固然各个函数的初始化工做有他们自身的函数完成了。函数
在include/linux/init.h文件中有引用到".initcall" level ".init"的地方post
/* initcalls are now grouped by functionality into separate * subsections. Ordering inside the subsections is determined * by link order. * For backwards compatibility, initcall() puts the call in * the device init subsection. * * The `id' arg to __define_initcall() is needed so that multiple initcalls * can point at the same handler without causing duplicate-symbol build errors. */ #define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn
而后调用__define_initcall的地方仍是在init.h文件中ui
/* * Early initcalls run before initializing SMP. * * Only for built-in code, not modules. */ #define early_initcall(fn) __define_initcall("early",fn,early) /* * A "pure" initcall has no dependencies on anything else, and purely * initializes variables that couldn't be statically initialized. * * This only exists for built-in code, not for modules. */ #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) #define __initcall(fn) device_initcall(fn)
不少子系统好比PCI, USB都会有一个名为subsys_initcall的入口,不过更多的是使用module_init函数。spa
module_init宏在MODULE宏有没有定义的状况下展开的内容是不一样的,若是这个宏没有定义,基本上代表阁下的模块是要编译进内核的(obj-y)。指针
1. 在MODULE没有定义这种状况下,module_init定义以下:#define module_init(x) __initcall(x);rest
由于
#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(x)最终展开为:
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:
static initcall_t __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) = gpio_init;
就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。
这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds及do_initcalls,那么不难理解阁下模块中的module_init中的初始化函数什么时候被调用了:在系统启动过程当中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。
若是MODULE这个宏没有定义,基本上代表该模块是要编译进内核的(obj-y)。因此module_exit(x)此时最终会被忽略掉,由于编译进入内核的模块是不须要进行清理工做的。code
2. 在MODULE被定义的状况下(大部分可动态加载的driver模块都属于此, obj-m)blog
module_init定义以下:#define module_init(initfn) \static inline initcall_t __inittest(void) \{ return initfn; } \int init_module(void) __attribute__((alias(#initfn)));这段宏定义关键点是后面一句,经过alias将initfn变名为init_module。前面那个__inittest的定义实际上是种技巧,用来对initfn进行某种静态的类型检查,若是阁下将模块初始化函数定义成,好比,void gpio_init(void)或者是int gpio_init(int),那么在编译时都会有相似下面的warning:GPIO/fsl-gpio.c: In function '__inittest':GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type经过module_init将模块初始化函数统一别名为init_module,这样之后insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址。若是objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。简言之,这种状况下模块的初始化函数在insmod时候被调用。