The initialization of various parts in the kernel boot process, such as midrange, page management, slab allocator, Task Scheduler, network, PCI device, and so on, can be divided into two types:
One is the key, initialization that must be done and must be done in a particular order, which is often the initialization of a non-critical subsystem (or module, function) that is directly like the kernel and is directly called, and is not loaded according to the configuration. can be built-in to the kernel executable file, or can be loaded in a modular way. But for this category, they also need the support of key subsystems of the kernel, or even some sort of dependency or sequential relationship between them, so their initialization needs to be implemented in a different way. For the second class subsystems, the kernel completes their initialization in the Do_initcalls, before which the kernel completes the initialization of the first class subsystem. Do_initcalls's call time is Start_kernel->rest_init->kernel_init->do_basic_setup->do_initcalls
First, Do_initcalls
1.1 Implementation Mode
The realization of this function embodies the design idea that Linux initializes the second kind of subsystem. Linux provides different "initialization levels", and Linux initializes at levels from high (level 0) to Low (7), which means that the class is initialized first and then the lower level is initialized. The code logic is simple:
int level;
for (level = 0, Level < Array_size (initcall_levels)-1; level++)
do_initcall_level (level);
Therefore, a module simply needs to define its own initialization function as a level, and the initialization function of the module is invoked when the level of initialization is do_initcalls.
For ease of use, the kernel uses the following macros to define each initialization level:
#define __define_initcall (FN, id) \ static initcall_t __initcall_# #fn # #id __used \ __attribute__ (__section__ (". initcal
L "#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 is statically initialized.
* This is exists for built-in code and 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_ini Tcall_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 (FN) device_i Nitcall (FN)
The above definition is for a non module, that is, a subsystem to be compiled into the impact of the kernel, and if it is a module, the definition becomes:
#define Early_initcall (FN) module_init (FN)
#define Core_initcall (FN) module_init (FN)
#define Postcore_initcall (FN) module_init (FN)
#define Arch_initcall (FN) module_init (FN)
#define Subsys_ Initcall (FN) module_init (FN)
#define Fs_initcall (FN) module_init (FN)
#define Device_initcall ( fn) module_init (FN)
#define Late_initcall (FN) module_init (FN)
#define Security_initcall (FN) module_init (FN)/* Each module must the use one
module_init (). * * *
#define MODULE_INIT (INITFN) \
Static inline initcall_t __inittest (void) \
{return initfn;} \
int init_module (void) __attribute__ ((alias (#initfn));
By the way, module_init in systems that do not support modules will become:
#define __initcall (FN) Device_initcall (FN)
#define MODULE_INIT (x) __initcall (x);
1.2 Memory Scheduling Do_initcalls is a pointer to the initialization function from a specific memory area and then calls it, so the initialization functions for each level defined above should be placed in a particular memory region. The initialization function for each level is placed in its own specific initialization area.
Linux provides a header file "Vmlinux.lds.h", which defines macros to assist in writing connection scripts, from which you can see the individual memory sections that will eventually appear in the connection script and their relative positions, defining each "initialization level" The relevant code for the section of memory in which it resides is as follows:
#define Init_data_section (initsetup_align) \
. Init.data:AT (ADDR (. init.data)-load_offset) { \
Init_data \
init_setup (initsetup_align) \
init_calls \
con_initcall \
Security_initcall \
init_ram_fs \
}
The macro defines the initialization data segment, which includes the Init_calls segment.
#define INIT_CALLS_LEVEL (level) \
Vmlinux_symbol (__initcall# #level # #_start) =.; \
* (. initcall# #level ##.init) \
* (. initcall# #level # #s. init) \
#define Init_calls \
Vmlinux_symbol (__initcall_start) =.; \
* (. initcallearly.init) \
init_calls_level (0) \
init_calls_level (1) \
Init _calls_level (2) \
init_calls_level (3) \
init_calls_level (4) \
Init_calls_level (5 ) \
init_calls_level (rootfs) \
init_calls_level (6) \
init_calls_level (7) \
Vmlinux_symbol (__initcall_end) =.;
The macro defines the section where each initialization level is located, such as Init_calls_level (5), which is expanded to:
Vmlinux_symbol (__initcall5_start) =.;
* (. Initcall5.init)
* (. initcall5s. Init)
__initcall5_start is a variable that is used by Do_initcalls to locate the starting address of the initialization level 5, and then find all the initialization functions defined as initialization level 5 and call them.
It can also be seen from the init_calls that all levels of initialization functions are placed in memory areas between __initcall_start and __initall_end.
Other memory area definitions can also be found in the header file "Vmlinux.lds.h", and you can view the file if you are interested. second, initialization parameters 2.1 initialization parameter definition and initialization timing
Linux allows users to specify kernel options (that is, startup parameters) at startup, and these parameters are passed to the corresponding handler function. There are three different ways to define kernel options:
Using the macro Early_param (str, FN), for a module, the macro is defined as NULL using the macro __setup (str, FN), and for the module, the macro is defined as NULL using the macro Module_param (name, type, perm) in the first two ways, STR is the name of the option to be defined, and its value (that is, the part after "=") will be passed to FN for processing. The third way does not have function options, because in the new kernel, the kernel provides a kernel_param_ops structure for each data type, which holds the Set/get/free function for the data type, and therefore does not require each module to provide its own function. If you need to define a kernel option, you only need to provide the name, type, and desired access rights.
Kernel options defined in the first way are parse_early_param processed at the start of the kernel startup. The other two methods are handled by the following calls:
Parse_args ("Booting kernel", Static_command_line, __start___param, __stop___param
-__start___param,
-1,-1, &unknown_bootoption);
The meaning of the Parse_args parameter:
First parameter: A string that prompts for the kernel option being resolved the second parameter: command-line argument third parameter: The first address of the parameter to resolve. The parameters to parse are the parameters defined by the Module_param, which are stored in the __param segment, and the fourth parameter of the relevant definition is given: How many parameters are to be resolved, the fifth and sixth parameters: the minimum and maximum levels of the parameters, Only the parameters that fall within this hierarchy will be parsed. The last argument is a function: To specify who should handle a parameter if it is not recognized, where unknown_bootoption is used, which handles parameters defined using __setup (str, FN)
In fact Parse_early_param will eventually call Parse_args, but in its invocation, the processing function used for unrecognized parameters is Do_early_param.
Module_param will eventually pass the following macro definition;
#define __module_param_call (prefix, name, OPS, ARG, perm, level) \//
* Default value instead of permissions? */
\
static int __param_perm_check_# #name __attribute__ ((unused)) = \
Build_bug_on_zero ((perm) < 0 | | (perm) > 0777 | | ((perm) & 2)) \
+ build_bug_on_zero (sizeof ("prefix") > Max_param_prefix_len); \
Static const char __param_str_# #name [] = prefix #name; \
static struct Kernel_param __moduleparam_const __param_# #name \
__used \
__attribute__ ( (Unused,__section__ ("__param"), aligned (sizeof (void *))) \
= {__param_str_# #name, OPS, perm, level, {arg}}
2.2 Memory scheduling of initialization parameters
The initialization parameters defined by Early_param and __setup are placed in the. Init.setup section, and the specific code can view the header file "init.h". The definition of the Init.setup segment is also in the header file "Vmlinux.lds.h" as follows
#define INIT_SETUP (initsetup_align) \
. = Align (initsetup_align); \
Vmlinux_symbol (__setup_start) =.; \
* (. init.setup) \
Vmlinux_symbol (__setup_end) =.;
As can be seen from the definition of the above memory segment, the memory area defined by the macro begins at __setup_start and ends at __setup_end.
Parameters defined by Module_param are placed in the. Para segment, which can also be found in the header file "Vmlinux.lds.h" as follows:
__param:at (ADDR (__param)-load_offset) { \
vmlinux_symbol (__start___param) =.; \
* (__param) \
Vmlinux_symbol (__stop___param) =.; \
As can be seen from the definition of the above memory segment, the memory area defined by the macro begins at __start___param and ends at __stop___param.
2.3 Some other macros
#define __exit __section (. exit.text) __exitused __cold notrace
If a function adds a tag, Then it is put into the. Exit.text segment, which is special and is used for the module's exit function, if a module is compiled into a kernel image instead of being compiled as a module, that part of memory can be ignored by the linker, either by not connecting it to the kernel or by releasing the memory it consumes when the system is started, and if the kernel does not support Module Uninstall, you can also release the memory occupied by the part of the zone either at the time of the link or after startup.
The following macros directly give comments in the kernel code, and a simple memory area with these tags is free to save memory.
/* These are for everybody (although does all archs'll actually
discard it in modules) */
#define __INIT __se Ction (. Init.text) __cold notrace
#define __initdata __section (. init.data)
#define __INITCONST __ Constsection (. init.rodata)
#define __exitdata __section (. exit.data)
#define __exit_call __used _ _section (. Exitcall.exit)
Third, Inittab
After the kernel boot completes, it starts the first process init process. Inittab is the configuration file for the Init process, which is formatted as follows:
Id:runlevels:action:process some of the parts can be empty
Id:1~2 a character that uniquely identifies the row and cannot be duplicated in the configuration file. Runlevels: Configure the run level that the row applies to, where you can fill in multiple run levels, such as 12345 or 35. Linux has 7 running levels, as follows: 0: shutdown. 1: Single user character interface. 2: Multi-user character interface without network File system (NFS) functionality. 3: Multi-user character interface with network function. 4: Keep not. 5: A graphical user interface with network functions. 6: Reboot the system. Action: Action taken on the PROCESS,INIT process specified by the line. Init has the following behaviors: respawn: Start and monitor the process specified in item 4th, and then restart it if the process terminates wait: Execute the process specified in item 4th, waiting for it to finish once: Execute the process specified in 4th Boot: No matter which execution level, the system will run the 4th specified process bootwait at boot time: Regardless of the execution level, the system starts with the 4th specified process and waits until it performs a complete off: closes any action, which is equivalent to ignoring the configuration line OnDemand: When entering the OnDemand execution level, perform the process Initdefault specified in item 4th : The execution level entered after system startup, which does not require process Sysinit to be specified: Regardless of the execution level, The process powerwait specified in item 4th will be executed before boot and bootwait are executed: When the system is low on power, execute the 4th specified process and wait until it is finished Powerokwait: Executes the process specified in item 4th when the power supply of the system resumes normal, and waits until it finishes Powerfailnow: Perform the process Ctrlaltdel specified in the 4th when the system is severely short of power: when the user presses CTRL + Alt+del Action Kbrequest: When a user presses a special key combination, executes the process specified in item 4th, which is defined in the Keymaps file to define process: processes, which can be specified with parameters particular attention is given to the Initdefault, which specifies the level of execution that is entered after the system is started, and therefore does not require a process to be specified.