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() 
  1. under circumstances should use them?
  2. 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

Popular posts from this blog

html - How to style widget with post count different than without post count -

How to remove text and logo OR add Overflow on Android ActionBar using AppCompat on API 8? -

javascript - storing input from prompt in array and displaying the array -