Linux kernel module的module init function 如下,本文將以kernel module為例,說明他們是怎麼被kernel核心呼叫到。
static int __devinit tegra_kbc_init(void) {
pr_debug("KBC: tegra_kbc_init\n")
return platform_driver_register(&tegra_kbc_driver)
}
module_init(tegra_kbc_init)
module_init
隨便找到有呼叫module_init的kernel module往上追,可以找到最後是呼叫到__define_inicall(level,fn,id)這個macro,如下
// File: include/linux/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(tegra_kbc_init)會被展開成
static initcall_t __initcall_tegra_kbc_init6 __used
__attribute__((__section__(".initcall6.init"))) = tegra_kbc_init
由__attribute__得知,它最後會被放到initcall6.init這個section裡。而linker連結時會根據vmlinux.lds.S來將它放到指定的section。如下的INIT_CALLS。
// File: arch/arm/kernel/vmlinux.lds.S
SECTIONS {
INIT_SETUP(16)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
再往下追就看到了.initcall6.init。
// File: include/asm-generic/vmlinux.lds.h
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
INITCALLS \
VMLINUX_SYMBOL(__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)
do_initcalls
kernel是從那裡呼叫到init calls,可以從kernel的entry point,init/main.c,找到下面這個函式。
// File: init/main.c
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
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();
}
一路往上追可以知道do_initcalls()在start_kernel()的最後的函式裡面用一個kernel thread間接呼叫,這裡很容易就可以追到了,我就不再列出。
No comments:
Post a Comment