內核選項的解析完成以後,各個子系統的初始化即進入第二部分—入口函數的調用。一般USB、PCI這樣的子系統都會有一個名爲subsys_initcall的入口,若是你選擇它們做爲研究內核的切入點,那麼就請首先找到它。linux
C 語言中attribute屬性的section是在目標文件連接時能夠用於主動定製代碼的位置,具體能夠WIKI,下面看linux kernel中是如何定義的。
ide
如下代碼來自 linux內核源碼中 include/linux/init.h 文件。下面使用相同語法規則的變量名存放了各個初始化函數的地址。函數
更重要的是其section屬性也是按照必定規則構成的。
post
關於section見 http://lihuize123123.blog.163.com/blog/static/878290522010420111428109/ui
/* 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 /* * 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) #define __exitcall(fn) \ static exitcall_t __exitcall_##fn __exit_call = fn #define console_initcall(fn) \ static initcall_t __initcall_##fn \ __used __section(.con_initcall.init) = fn #define security_initcall(fn) \ static initcall_t __initcall_##fn \ __used __section(.security_initcall.init) = fn
這些入口有個共同的特徵,它們都是使用__define_initcall宏定義的。它們的調用也不是隨便的,而是按照必定順序的,這個順序就取決於__define_initcall宏。__define_initcall宏用來將指定的函數指針放到.initcall.init節裏。spa
.initcall.init節debug
內核可執行文件由許多連接在一塊兒的對象文件組成。對象文件有許多節,如文本、數據、init數據、bass等等。這些對象文件都是由一個稱爲連接器腳本的文件連接並裝入的。這個連接器腳本的功能是將輸入對象文件的各節映射到輸出文件中;換句話說,它將全部輸入對象文件都連接到單一的可執行文件中,將該可執行文件的各節裝入到指定地址處。 vmlinux.lds是存在於arch/<target>/目錄中的內核連接器腳本,它負責連接內核的各個節並將它們裝入內存中特定偏移量處。在vmlinux.lds文件裏查找initcall.init就能夠看到下面的內容指針
#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)
這就告訴咱們.initcall.init節又分紅了7個子節,而xxx_initcall入口函數指針具體放在哪個子節裏邊兒是由xxx_initcall的定義中,__define_initcall宏的參數決定的,好比core_initcall將函數指針放在.initcall1.init子節,device_initcall將函數指針放在了.initcall6.init子節等等。各個子節的順序是肯定的,即先調用.initcall1.init中的函數指針再調用.initcall2.init中的函數指針,等等。不一樣的入口函數被放在不一樣的子節中,所以也就決定了它們的調用順序。code
注意:設備驅動程序中常見的module_init(x)函數,查看init.h文件發現對象
/** * module_init() - driver initialization entry point * @x: function to be run at kernel boot time or module insertion * * module_init() will either be called during do_initcalls() (if * builtin) or at module insertion time (if a module). There can only * be one per module. */ #define module_init(x) __initcall(x); #define __initcall(fn) device_initcall(fn) /* Don't use these in modules, but some people do... */ #define early_initcall(fn) module_init(fn) #define core_initcall(fn) module_init(fn) #define postcore_initcall(fn) module_init(fn) #define arch_initcall(fn) module_init(fn) #define subsys_initcall(fn) module_init(fn) #define fs_initcall(fn) module_init(fn) #define device_initcall(fn) module_init(fn) #define late_initcall(fn) module_init(fn)
#define __define_initcall(level,fn) \ static initcall_t __initcall_##fn __used \ __attribute__((__section__(".initcall" level ".init"))) = fn /* Userspace initcalls shouldn't depend on anything in the kernel, so we'll * make them run first. */ #define __initcall(fn) __define_initcall("1", fn) #define __exitcall(fn) static exitcall_t __exitcall_##fn __exit_call = fn #define __init_call __used __section(.initcall.init)
這樣推斷 module_init 調用優先級爲6低於subsys_initcall調用優先級4
static void __init do_initcalls(void) { initcall_t *fn; for (fn = __early_initcall_end; fn < __initcall_end; fn++) do_one_initcall(*fn); /* Make sure there is no pending stuff from the initcall sequence */ flush_scheduled_work(); } int __init_or_module do_one_initcall(initcall_t fn) { int count = preempt_count(); int ret; if (initcall_debug) ret = do_one_initcall_debug(fn); else ret = fn(); msgbuf[0] = 0; if (ret && ret != -ENODEV && initcall_debug) sprintf(msgbuf, "error code %d ", ret); if (preempt_count() != count) { strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf)); preempt_count() = count; } if (irqs_disabled()) { strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf)); local_irq_enable(); } if (msgbuf[0]) { printk("initcall %pF returned with %s\n", fn, msgbuf); } return ret; }