linux kernel - module_init() vs. core_initcall() vs. early_initcall() -
in drivers see these 3 types of init functions being used.
module_init() core_initcall() early_initcall()
- under circumstances should use them?
- also, there other ways of init?
they determine initialization order of built-in modules. drivers use device_initcall
(or module_init
; see below) of time. initialization (early_initcall
) used architecture-specific code initialize hardware subsystems (power management, dmas, etc.) before real driver gets initialized.
technical stuff understanding below
look @ init/main.c
. after few architecture-specific initialization done code in arch/<arch>/boot
, arch/<arch>/kernel
, portable start_kernel
function called. eventually, in same file, do_basic_setup
called:
/* * ok, machine initialized. none of devices * have been touched yet, cpu subsystem , * running, , memory , process management works. * * can start doing 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(); }
which ends call do_initcalls
:
static initcall_t *initcall_levels[] __initdata = { __initcall0_start, __initcall1_start, __initcall2_start, __initcall3_start, __initcall4_start, __initcall5_start, __initcall6_start, __initcall7_start, __initcall_end, }; /* keep these in sync initcalls in include/linux/init.h */ static char *initcall_level_names[] __initdata = { "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); (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn); } static void __init do_initcalls(void) { int level; (level = 0; level < array_size(initcall_levels) - 1; level++) do_initcall_level(level); }
you can see names above associated index: early
0, core
1, etc. each of __initcall*_start
entries point array of function pointers called 1 after other. function pointers actual modules , built-in initialization functions, ones specify module_init
, early_initcall
, etc.
what determines function pointer gets __initcall*_start
array? linker this, using hints module_init
, *_initcall
macros. macros, built-in modules, assign function pointers specific elf section.
example module_init
considering built-in module (configured y
in .config
), module_init
expands (include/linux/init.h
):
#define module_init(x) __initcall(x);
and 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)
. _define_initcall
:
#define __define_initcall(fn, id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" #id ".init"))) = fn
which means, far, have:
static initcall_t __initcall_my_func6 __used __attribute__((__section__(".initcall6.init"))) = my_func;
wow, lots of gcc stuff, means new symbol created, __initcall_my_func6
, that's put in elf section named .initcall6.init
, , can see, points specified function (my_func
). adding functions section creates complete array of function pointers, stored within .initcall6.init
elf section.
initialization example
look again @ chunk:
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn);
let's take level 6, represents built-in modules initialized module_init
. starts __initcall6_start
, value being address of first function pointer registered within .initcall6.init
section, , ends @ __initcall7_start
(excluded), incrementing each time size of *fn
(which initcall_t
, void*
, 32-bit or 64-bit depending on architecture).
do_one_initcall
call function pointed current entry.
within specific initialization section, determines why initialization function called before order of files within makefiles since linker concatenate __initcall_*
symbols 1 after other in respective elf init. sections.
this fact used in kernel, e.g. device drivers (drivers/makefile
):
# gpio must come after pinctrl gpios may need mux pins etc obj-y += pinctrl/ obj-y += gpio/
tl;dr: linux kernel initialization mechanism beautiful, albeit highlight gcc-dependent.
Comments
Post a Comment