linux 3.7.2
init/main.c
asmlinkage void __init start_kernel(void) // 進入點
{
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
tick_init();
boot_cpu_init();
page_address_init();
printk(KERN_NOTICE "%s", linux_banner); // 理論上kernel 第一行印出.....
..........................................
/* Do the rest non-__init'ed, we're now alive */
rest_init(); // 最後這行才會去init device.....
}
static noinline void __init_refok rest_init(void)
{
rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); // kernel_init 是pfn
}
static int __ref kernel_init(void *unused)
{
kernel_init_freeable();
......
}
static void __init kernel_init_freeable(void)
{
......
do_basic_setup(); //
......
}
/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/
static void __init do_basic_setup(void)
{
cpuset_init_smp();
usermodehelper_init();
shmem_init();
driver_init();
init_irq_proc();
do_ctors();
usermodehelper_enable();
do_initcalls(); // 這裡才是真正執行initial device
}
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = { // 這是initcall level, 共0~7, 依序去執行
"early",
"core",
"postcore",
"arch",
"subsys",
"fs",
"device",
"late",
};
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);
}
在kernel driver/module常看到module_init(fun1), early_initcall(fun2)這種宣告方式
就是把 fun2 加到 early level 的 list,
呼叫同一 level 的 function 順序是在linkage加入的順序.....
ex:
以下參考: http://stackoverflow.com/questions/18605653/linux-module-init-vs-core-initcall-vs-early-initcall
以下的訊息得知module_init()最後是加到 "device" 這個level......
Example with module_init
Considering a built-in module (configured with y in .config), module_init simply expands like this (include/linux/init.h):
#define module_init(x) __initcall(x);
and then we follow this:
#define __initcall(fn) device_initcall(fn)#define device_initcall(fn) __define_initcall(fn,6)
So, now, module_init(my_func) means __define_initcall(my_func, 6). This is _define_initcall:
#define __define_initcall(fn, id) \
staticinitcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall"#id ".init"))) = fn
which means, so far, we have:
staticinitcall_t __initcall_my_func6 __used
__attribute__((__section__(".initcall6.init")))= my_func;
Wow, lots of GCC stuff, but it only means that a new symbol is created, __initcall_my_func6, that's put in the ELF section named .initcall6.init, and as you can see, points to the specified function (my_func). Adding all the functions to this section eventually creates the complete array of function pointers, all stored within the .initcall6.init ELF section.
Initialization example
Look again at this chunk:
for(fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
Let's take level 6, which represents all the built-in modules initialized with module_init. It starts from __initcall6_start, its value being the address of the first function pointer registered within the .initcall6.init section, and ends at __initcall7_start (excluded), incrementing each time with the size of *fn (which is an initcall_t, which is a void*, which is 32-bit or 64-bit depending on the architecture).
do_one_initcall will simply call the function pointed to by the current entry.
Within a specific initialization section, what determines why an initialization function is called before another is simply the order of the files within the Makefiles since the linker will concatenate the __initcall_* symbols one after the other in their respective ELF init. sections.
This fact is actually used in the kernel, e.g. with device drivers (drivers/Makefile):
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y += pinctrl/
obj-y += gpio/
