基于aarch64分析kernel源码 六:kernel_init进程(1号进程)、kthreadd进程(2号进程)

一、kernel_init进程创建流程

start_kernel 
	--> arch_call_rest_init 
		--> rest_init
			--> pid = user_mode_thread(kernel_init, NULL, CLONE_FS)
				---> kernel_clone

二、kernel_init

static int __ref kernel_init(void *unused)
{
	int ret;

	/*
	 * Wait until kthreadd is all set-up.
	 * 等到 kthreadd 全部设置完毕。
	 */
	wait_for_completion(&kthreadd_done);

	kernel_init_freeable();
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();

	system_state = SYSTEM_FREEING_INITMEM;
	kprobe_free_init_mem();
	ftrace_free_init_mem();
	kgdb_free_init_mem();
	exit_boot_config();
	free_initmem();
	mark_readonly();

	/*
	 * Kernel mappings are now finalized - update the userspace page-table
	 * to finalize PTI.
	 */
	pti_finalize();

	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	rcu_end_inkernel_boot();

	do_sysctl_args();

	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.
	 * 如果我们试图恢复一台真正损坏的机器,可以使用 Bourne shell 代替 init。
	 */
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}

	if (CONFIG_DEFAULT_INIT[0] != '\0') {
		ret = run_init_process(CONFIG_DEFAULT_INIT);
		if (ret)
			pr_err("Default init %s failed (error %d)\n",
			       CONFIG_DEFAULT_INIT, ret);
		else
			return 0;
	}

	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/admin-guide/init.rst for guidance.");
}

1、执行初始化函数。

2、如果指定 ramdisk_execute_command,执行 ramdisk_execute_command 初始化程序。

3、如果指定 execute_command,执行 execute_command 初始化程序。

4、如果指定 CONFIG_DEFAULT_INIT,执行 CONFIG_DEFAULT_INIT 变量中保存的初始化程序。

5、依次尝试执行 /sbin/init/etc/init/bin/init/bin/sh 程序。

三、kthreadd 进程创建流程

start_kernel 
	--> arch_call_rest_init 
		--> rest_init
			--> pid = kernel_thread(kthreadd, NULL, NULL, CLONE_FS | CLONE_FILES)
				---> kernel_clone

四、kthreadd

int kthreadd(void *unused)
{
	struct task_struct *tsk = current;

	/* Setup a clean context for our children to inherit. */
	set_task_comm(tsk, "kthreadd");
	ignore_signals(tsk);
	set_cpus_allowed_ptr(tsk, housekeeping_cpumask(HK_TYPE_KTHREAD));
	set_mems_allowed(node_states[N_MEMORY]);

	current->flags |= PF_NOFREEZE;
	cgroup_init_kthreadd();

	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (list_empty(&kthread_create_list))
			schedule();
		__set_current_state(TASK_RUNNING);

		spin_lock(&kthread_create_lock);
		while (!list_empty(&kthread_create_list)) {
			struct kthread_create_info *create;

			create = list_entry(kthread_create_list.next,
					    struct kthread_create_info, list);
			list_del_init(&create->list);
			spin_unlock(&kthread_create_lock);

			create_kthread(create);

			spin_lock(&kthread_create_lock);
		}
		spin_unlock(&kthread_create_lock);
	}

	return 0;
}

kthreadd 利用 for(;;) 一直驻留在内存中运行,主要过程如下:

  • 检查 kthread_create_list 为空时,kthreadd 让出 cpu 的执行权。
  • kthread_create_list 不为空时,利用 while 循环遍历 kthread_create_list 链表。
  • 每取下一个链表节点后调用 create_kthread,创建内核线程。

所有的内核线程都是 2 号进程创建的。

你可能感兴趣的:(Linux内核调试,kernel)