linux thread init
(note)
-
The code of this article comes from the linux kernel version: 4.1.15 (Xiaosheng's articles will be based on this version unless otherwise specified)
-
The source file of this code starts with (/), which represents the installation directory of linux kernel. (unless otherwise specified in Xiaosheng's articles, there is such an agreement)
1, Overview
At rest_ Kthread will be called in init() function_ The init() function creates an init thread, as shown in the following code:
kernel_thread(kernel_init, NULL, CLONE_FS);
In the linux kernel, the kernel_init represents init thread 1. When the linux kernel is started, the init program in user space will be started to change the linux kernel from kernel state to user state. Therefore, this article will analyze init threads.
2, The entry function kernel of init thread_ init()
The entry function of init thread is kernel_init, as follows (/ init/main.c):
static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); async_synchronize_full(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); flush_delayed_fput(); if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); } /* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */ if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; panic("Requested init %s failed (error %d).", execute_command, ret); } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance."); }
3, Kernel_ init_ Freeable analysis
In kernel_ Init() the kernel will be called in the init thread function_ init_ Freeable() function:
static noinline void __init kernel_init_freeable(void) { /* Wait until all kthreadd threads are started */ wait_for_completion(&kthreadd_done); /* The code runs here, the scheduler scheduler has been fully started, and now it starts blocking allocation */ gfp_allowed_mask = __GFP_BITS_MASK; /* Set the init thread to allocate pages on any node */ set_mems_allowed(node_states[N_MEMORY]); /* Set the init thread to run on any cpu */ set_cpus_allowed_ptr(current, cpu_all_mask); /* Gets the pid of the current thread */ cad_pid = task_pid(current); //Under smp, perform the preparation operation of starting other CPUs smp_prepare_cpus(setup_max_cpus); //Initialization call before executing smp do_pre_smp_initcalls(); lockup_detector_init(); //SMP initialization. Called by the boot processor to activate the remainder smp_init(); //SMP scheduling initialization. sched_init_smp(); //Perform some basic startup operations. do_basic_setup(); //Open / dev/console in rootfs. This place cannot fail if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) pr_err("Warning: unable to open an initial console.\n"); (void) sys_dup(0); (void) sys_dup(0); /* Check whether the early init program for space is set. If so, let it execute. ramdisk_ execute_ The command can be specified by "rdinit =" */ if (!ramdisk_execute_command) ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); } integrity_load_keys(); load_default_modules(); }
Line 31 of the above code: call do_basic_setup() performs some basic startup operations. This function is very important in the process of linux kernel startup. It mainly involves the initialization of important components of linux kernel, such as the initialization of driver model and so on.
Lines 37-38, use sys_ The dup () system call function copies the file descriptor of standard input twice, one as standard output and one as standard error. Through this operation, the standard input, output and error are set to / dev/console. During development, the console parameter can be set through the bootargs environment variable of u-boot to specify the console parameter!!!
Line 41-42: check whether the early init program for space is set. If so, let it execute. ramdisk_ execute_ The command can be specified by "rdinit =". If ramdisk_ execute_ If the command is not executed, linux sets it to / init.
Line 44-47 Code: through sys_ The access system call checks whether the process can access the pathname of the file (specified by ramdisk_execute_command). Call prepare_namespace(); To mount the root file system. The root file system can also be specified by the environment variable parameter under the command line of u-boot. For example, "root=/dev/mmcblk1p2 rootwait rw" means that the root file system is in / dev/mmcblk1p2, that is, partition 2 of EMMC.
Line 49-50: when the code runs here, the linux kernel has completed the initial startup process and has actually started and run. Next, you need to release the memory space occupied by the initmem segment and enter the user space mode. This part is controlled by the kernel_ init_ The subsequent code of the freeable() function is complete. rootfs root file system is now available, call integrity_load_keys() attempts to load the public key and calls load_default_modules() loads the default module.
The following will return to the kernel_init() function to continue the subsequent analysis.
4, Kernel_ Analysis of the rest of init()
When kernel_ init_ After the freeable () function is executed, some basic components of the linux kernel, such as the device model, the root file system and are ready. Next, the user space program will be started. Thus, the linux kernel changes from kernel state to user state.
(4-1) some basic operations
The following code snippet:
async_synchronize_full(); free_initmem(); //(this function seems to be related to debugging. Xiaosheng doesn't know what effect it has. haha) mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); flush_delayed_fput();
(1)async_synchronize_full() function: used to synchronize all asynchronous calls.
(2)free_initmem() function: used to free the memory space during initialization.
(3) Using system_state = SYSTEM_RUNNING; Set the system status to SYSTEM_RUNNING.
(4)numa_default_policy() function (/ mm/mempolicy.c): resets the NUMA storage management policy of the current thread to the default value.
(5)flush_delayed_fput() function: if the kernel thread really needs to complete the last fput() it has completed, this function will be called. In kernel_ Now the only operation in the init() function is boot. Therefore, it is necessary to ensure that the write operation to the binary file on initramfs will not cause the open struct file to wait__ fput(). Without this flush_delayed_fput(), execve() will not work.
(4-2) start ramdisk_ execute_ Program specified by command
(Note: the following code snippet comes from the kernel_init() function)
if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); }
ramdisk_execute_command is a global char pointer variable with a value of "/ init", that is, the init program in the root directory. ramdisk_ execute_ The command can be specified in the environment variable through uboot, and can be specified by using "rdinit=xxx" in the bootargs of u-boot, where xxx is the specific init program name.
If ramdisk_ execute_ If the command is specified (that is, it is not NULL), call run_ init_ The process() function starts it and exits if it starts successfully.
(4-3) start execute_ Program specified by command
(Note: the following code snippet comes from the kernel_init() function)
if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; panic("Requested init %s failed (error %d).", execute_command, ret); }
Similarly, execute_command is also a global char pointer variable. The value can be passed through u-boot. The following code fragment (/ init/main.c):
static int __init init_setup(char *str) { unsigned int i; execute_command = str; for (i = 1; i < MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } __setup("init=", init_setup);
As can be seen from the above code, you can specify execute by using "init=xxxx" in the startup parameter bootargs of u-boot_ The value of command, such as "init=/linuxrc", indicates that linuxrc in the root file system is the init program in user space to be executed.
Similarly, if execute_ If command is specified, run is called_ init_ Process() starts the program and exits if it is started successfully.
(4-4) start the default user space program of linux kernel
When the init program of user space is not started in the first two methods, the default program of linux kernel will be executed.
(Note: the following code snippet comes from the kernel_init() function)
if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0;
It can be seen that four user space programs are specified by default in the linux kernel: / sbin/init, / etc/init, / bin/init, / bin/sh. Call try_ to_ run_ init_ The process () function starts them. If any of these programs are started, exit.
(4-5) linux kernel "strike"
When the init process in user space has not been started through the above three methods. Then panic is called to give the information
panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance.");
So far, the linux kernel has not started the init program in user space. The system stopped / went on strike.....
At work, when you see this message, it means that the code has run here. Both excited and upset!!!
Xiaosheng has limited knowledge and energy. If there is anything wrong with the article, please comment more. You can also communicate with me (e-mail: iriczhao@163.com ), O(∩ ∩) O thank you.