linux模組(module_init)、子系統(subsys_initcall)入口函式
一、Include/linux/init.h中:
/* 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(fn, id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" #id ".init"))) = fn /* * Early initcalls run before initializing SMP. * * Only for built-in code, not modules. */ #define early_initcall(fn) __define_initcall(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. * Keep main.c:initcall_level_names[] in sync. */ #define pure_initcall(fn) __define_initcall(fn, 0) #define core_initcall(fn) __define_initcall(fn, 1) #define core_initcall_sync(fn) __define_initcall(fn, 1s) #define postcore_initcall(fn) __define_initcall(fn, 2) #define postcore_initcall_sync(fn) __define_initcall(fn, 2s) #define arch_initcall(fn) __define_initcall(fn, 3) #define arch_initcall_sync(fn) __define_initcall(fn, 3s) #define subsys_initcall(fn) __define_initcall(fn, 4) #define subsys_initcall_sync(fn) __define_initcall(fn, 4s) #define fs_initcall(fn) __define_initcall(fn, 5) #define fs_initcall_sync(fn) __define_initcall(fn, 5s) #define rootfs_initcall(fn) __define_initcall(fn, rootfs) #define device_initcall(fn) __define_initcall(fn, 6) #define device_initcall_sync(fn) __define_initcall(fn, 6s) #define late_initcall(fn) __define_initcall(fn, 7) #define late_initcall_sync(fn) __define_initcall(fn, 7s)
其中我們發現了一系列的入口函式,這些函式其實都是對__define_initcall的封裝。
__define_initcall中的第二個引數是呼叫順序,數字越小在linux啟動過程中呼叫的越早。
可以看到我們熟悉的模組入口函式device_initcall的優先順序是6,subsys_initcall的優先順序是4.
這個巨集定義是將我們定義的模組初始化函式按照初始化優先順序放到相應的.initcallX.init段中,如subsys_initcall定義的函式放到.initcall4.init段中。
詳細解釋一下這個巨集的定義:
#define __define_initcall(fn, id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" #id ".init"))) = fn
1,__define_initcall(fn, id) 這個巨集有兩個引數fn、id
2,static initcall_t _initcall##fn##id __used 定義了一個initcall_t型別的函式指標。 initcall_t的定義為typedef int (*initcall_t)(void);,是一個引數為空,返回值為int的函式指標型別。
傳遞給module_init 的函式就是這個型別的static int hello_init(); 這個新定義的函式指標名稱是有fn和id兩個引數組成的_initcall##fn##id; __used告訴編譯器無論 GCC 是否發現這個函式的呼叫例項,都要使用這個函式。
3,attribute((section(“.initcall” #id “.init”))) = fn;
attribute((section(“”)))是為連結器指定連結的section,這裡按照id將各個初始化函式放置到不同的.initcallX.init段中。
然後將初始化函式指標賦值給了新定義的函式指標。
二、arch\arm\kernel\vmlinux.lds發現lds中定義的各個section的具體內容如下:
.init.data : {
*(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(8);
__clk_of_table = .; KEEP(*(__clk_of_table)) KEEP(*(__clk_of_table_end)) . = ALIGN(8);
__clksrc_of_table = .; KEEP(*(__clksrc_of_table)) KEEP(*(__clksrc_of_table_end)) . = ALIGN(32);
__dtb_start = .; KEEP(*(.dtb.init.rodata)) __dtb_end = .; . = ALIGN(8);
__irqchip_begin = .; KEEP(*(__irqchip_of_table)) KEEP(*(__irqchip_of_end)) . = ALIGN(16);
__setup_start = .; KEEP(*(.init.setup)) __setup_end = .;
__initcall_start = .; KEEP(*(.initcallearly.init))
__initcall0_start = .; KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init))
__initcall1_start = .; KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init))
__initcall2_start = .; KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init))
__initcall3_start = .; KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init))
__initcall4_start = .; KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init))
__initcall5_start = .; KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init))
__initcallrootfs_start = .; KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init))
__initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init))
__initcall7_start = .; KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init))
__initcall_end = .;
__con_initcall_start = .; KEEP(*(.con_initcall.init)) __con_initcall_end = .;
__security_initcall_start = .; KEEP(*(.security_initcall.init)) _security_initcall_end = .;
}
三、Init/main.c中:
asmlinkage void __init start_kernel(void)
{
static noinline void __init_refok rest_init(void)
{
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
static int __ref kernel_init(void *unused)
{
static noinline void __init kernel_init_freeable(void)
{
static void __init do_basic_setup(void)
{
extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
static void __init do_initcall_level(int level)
{
extern const struct kernel_param __start___param[], __stop___param[];
initcall_t *fn;
strcpy(static_command_line, saved_command_line);
parse_args(initcall_level_names[level],
static_command_line, __start___param,
__stop___param - __start___param,
level, level,
&repair_env_string);
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
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);
{
......
ret = fn();
......
}
else
ret = fn();
msgbuf[0] = 0;
if (preempt_count() != count) {
sprintf(msgbuf, "preemption imbalance ");
preempt_count() = count;
}
if (irqs_disabled()) {
strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
local_irq_enable();
}
WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);
return ret;
}
}
}
}
}
}
}
}