Analysis of Android boot process 1 Start instructions before INIT

insert image description here

1 Key process before INIT starts

1.1 bootloader startup process

第一阶段:硬件初始化,SVC模式,关闭中断,关闭看门狗,初始化栈,进入C代码
第二阶段:CPU/board/中断初始化;初始化内存与flash;将kernel从flash中拷贝到内存中;执行bootm,启动内核

The purpose of the entire bootloader storage startup is to initialize the flash, copy the kernel to the memory, and execute bootm to start the kernel

1.2 Kernel startup process

start_kernel()
->rest_init(){
    
    创建2个进程}
-->kernel_init
--->执行保存在__initcall_start与__early_initcall_end之间的函数
--->smp 多核初始化处理
--->内核驱动模块初始化
--->init_post->run_init_process->kernel_execve启动init,一个用户态线程
-->另一个kthreadd,一个内核态线程

The purpose of kernel startup is each hardware device. Finally, two key threads are started, one is init in user mode, and the other is kthreadd in kernel mode.

2 Interpretation of the overall process

{Here first describe the startup process from start_kernel to init, the picture is very complete, in this part we only focus on the upper part}
insert image description here

2.1 Linux starts the kernel by calling the start_kernel function. When the kernel startup module is started, it will start the first process in the user space

(The Linux kernel creates 2 processes at the same time during the startup process: Process 1: a kernel process named Kthreadd, PID=2, used to create other processes in the kernel space; {Part of the kernel process such as binder, dhd_watchdog and other processes
PPID =2, indicating that these processes are all created by the kthreadd process:}
Process 2: the first user space Init process, the process PID = 1, used to start some local processes
{such as the Zygote process, and the Zygote process is also a dedicated A native process that incubates a Java process })

2.2 INI process startup code analysis
2.2.1 During the boot process of the Linux kernel, Start_kernel will be called to initialize the configuration:
asmlinkage void __init start_kernel(void)
{
    
    
    ...//执行初始化工作
    rest_init(); 
}

The start_kernel function calls some initialization functions After completing the initialization work, call the rest_init() function to create a new process:

static noinline void __init_refok rest_init(void) __releases(kernel_lock)
{
    
    
	int pid;
	rcu_scheduler_starting();
    //创建一个kernel_init进程,该进程实质上是Init进程,用于启动用户空间进程
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); 
	
	numa_default_policy();
	
	//创建一个kthreadd内核线程,用于创建新的内核进程
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
 
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();
	complete(&kthreadd_done);
	unlock_kernel();
 
	//The boot idle thread must execute schedule() at least once to get things moving:
	init_idle_bootup_task(current);
	preempt_enable_no_resched();
	schedule(); 
	preempt_disable();
	cpu_idle();    //Call into cpu_idle with preempt disabled
}

Complete the creation of two new processes in the rest_init function: the Init process and the kthreadd process, because the Init process was created first, so its PID=1 and the PID of kthreadd=2, the kernel_thread function only
calls the fork system call to create a new process , both the created child process and the parent process execute the code after the fork function call, and the child process is a copy of the parent process. At the same time, here is the last thing to pay attention to how the init process starts

2.2.2 So continue to analyze the kernel_init function
static int __init kernel_init(void * unused)
{
    
    
	wait_for_completion(&kthreadd_done);
	set_mems_allowed(node_states[N_HIGH_MEMORY]);
	set_cpus_allowed_ptr(current, cpu_all_mask);
	cad_pid = task_pid(current);
	smp_prepare_cpus(setup_max_cpus);
    
	//执行保存在__initcall_start与__early_initcall_end之间的函数
	do_pre_smp_initcalls();
	lockup_detector_init();
	
        //smp 多核初始化处理
	smp_init();
	sched_init_smp();
    
	do_basic_setup();//内核驱动模块初始化
	/* Open the /dev/console on the rootfs, this should never fail */
	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
		printk(KERN_WARNING "Warning: unable to open an initial console.\n");
	(void) sys_dup(0);
	(void) sys_dup(0);
	/*
	 * check if there is an early userspace init.  If yes, let it do all
	 * the work
	 */
	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();
	}
	/*
	 * Ok, we have completed the initial bootup, and
	 * we're essentially up and running. Get rid of the
	 * initmem segments and start the user-mode stuff..
	 * 进入用户空间,执行用户空间代码
	 */
	init_post();
	return 0;
}

In the kernel_init function, call the function saved between __initcall_start to __initcall_end to initialize the driver module, and then directly call the init_post() function to enter the user space

2.2.3 Execute the init process code
static noinline int init_post(void)
{
    
    
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	mark_rodata_ro();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();
	current->signal->flags |= SIGNAL_UNKILLABLE;
    //如果ramdisk_execute_command不为空,ramdisk_execute_command下的Init程序
	if (ramdisk_execute_command) {
    
    
		run_init_process(ramdisk_execute_command);
		printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);
	}
    //如果execute_command不为空,execute_command下的Init程序
	if (execute_command) {
    
    
		run_init_process(execute_command);
		printk(KERN_WARNING "Failed to execute %s.  Attempting ""defaults...\n", execute_command);
	}
	//如果以上路径下都没有init程序,就从/sbin、/etc、/bin三个路径下寻找init程序,同时启动一个sh进程
	run_init_process("/sbin/init");
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");
    //如果以上路径都没有找到init程序,调用内核panic
	panic("No init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

When the init process does not exist in the top-level directory of the root file system, or the startup option "init=" is not specified, the kernel will search for init files in the /sbin, /etc, and /bin directories.
If the init file is still not found in these directories, the kernel will abort the execution of the init process and cause a Kernel Panic.
The run_init_process function jumps from the kernel space to the user space through the system call do_execve, and executes the entry function of the Init program of the user space.

static void run_init_process(const char *init_filename)
{
    
    
	argv_init[0] = init_filename;
	kernel_execve(init_filename, argv_init, envp_init);
}

Here is the introduction of the kernel startup process. The run_init_process function will execute the entry function of the Init program {The entry function of Init is located in /system/core/init/init.c }

2.2.4 Other instructions

Here is a brief explanation. Recently, I have been studying embedded Linux systems, and I have also done a process analysis related to boot optimization. In fact, booting will start in the init process. The difference lies in the architecture after init, which is completely different from the beginning.

Guess you like

Origin blog.csdn.net/wzx311/article/details/129790810