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/
留言列表