基于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
号进程创建的。