主要分析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時候被調用。