rest_init 与 kernel_init 机制与实现详解

rest_init 与 kernel_init 机制与实现详解

源码路径rk3588/kernel-6.1/init/main.c
内核版本:Linux 6.1(RK3588 / ARM64)
文档目录linuxDoc/init/


目录


一、原理

1.1 为何需要 rest_init

start_kernel() 运行在 init_task 上且长期关抢占。再继续在同一上下文里挂根、exec init 会阻塞 boot CPU。rest_init() 的职责是:

  1. 创建 将变为 PID 1kernel_init 内核线程
  2. 创建 kthreadd(其它内核线程的父线程)
  3. 让当前 boot 线程进入 idle

1.2 kernel_init 两阶段

阶段 函数 内容
可释放 init 内存前 kernel_init_freeable SMP、initcall、initramfs、挂根
之后 kernel_init 本体 free_initmemSYSTEM_RUNNINGkernel_execve

二、实现方式

2.1 rest_init()

1
2
3
4
5
6
7
8
9
10
noinline void __ref rest_init(void)
{
pid = user_mode_thread(kernel_init, NULL, CLONE_FS);
// PF_NO_SETAFFINITY,绑 boot CPU
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
complete(&kthreadd_done);
system_state = SYSTEM_SCHEDULING;
schedule_preempt_disabled();
cpu_startup_entry(CPUHP_ONLINE);
}

顺序约束:必须先有 kthreadd,否则 kernel_init 里创建 kthread 会 OOPS;因此 kernel_init 开头 wait_for_completion(&kthreadd_done)

2.2 kernel_init_freeable()

典型顺序:

1
2
3
4
5
6
7
8
9
10
11
gfp_allowed_mask = __GFP_BITS_MASK
smp_prepare_cpus → workqueue_init → init_mm_internals
do_pre_smp_initcalls()
smp_init() → sched_init_smp()
[可选] kthread_run(defer_free_memblock) // RK Thunder Boot
page_alloc_init_late()
do_basic_setup() // driver_init + do_initcalls
wait_for_initramfs()
console_on_rootfs()
若 ramdisk_execute_command 不可执行 → prepare_namespace()
integrity_load_keys()

2.3 kernel_init() 收尾

1
2
3
4
5
6
7
8
9
system_state = SYSTEM_FREEING_INITMEM;
free_initmem();
mark_readonly();
pti_finalize();
system_state = SYSTEM_RUNNING;
// exec 顺序:
// ramdisk_execute_command → init= → CONFIG_DEFAULT_INIT
// → /sbin/init → /etc/init → /bin/init → /bin/sh
panic("No working init found...");

run_init_process() 内部 kernel_execve,使用 argv_init / envp_init(含 init=-- 后参数)。

2.4 早期用户态 init(initramfs)

ramdisk_execute_command(默认常为 /init)存在且可访问,可能prepare_namespace 之前由 initramfs 脚本完成挂根;否则走 prepare_namespace()。逻辑在 kernel_init_freeable

1
2
3
4
if (init_eaccess(ramdisk_execute_command) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}

三、关键数据结构与接口

符号 含义
kthreadd_done completion,同步 kthreadd 就绪
execute_command init= 启动参数
ramdisk_execute_command initramfs 中 init 路径
system_state SYSTEM_SCHEDULINGFREEING_INITMEMRUNNING
cad_pid Ctrl-Alt-Del 处理用 pid(在 freeable 中设置)
kthreaddkernel_initboot 线程kthreaddkernel_initboot 线程user_mode_threadkernel_threadcpu_idlewait kthreadd_donekernel_init_freeablefree_initmem exec init

四、RK3588 平台说明

CONFIG_ROCKCHIP_THUNDER_BOOT_DEFER_FREE_MEMBLOCK 时,在 smp_init() / sched_init_smp() 之后启动 defer_mem 内核线程,将 memblock 延迟释放与 buddy 初始化并行。详见 07

编解码板卡上 PID 1 多为 systemd 或 busybox;内核只负责 exec,服务编排不在 init/


五、调试与观测

现象 排查
卡在 Freeing unused kernel memory 之前 initcall、wait_for_initramfs
No working init found init= 路径、rootfs 是否含 /sbin/init
重复挂根 initramfs /init 是否已 pivot_root
Failed to execute ... (error -2) ENOENT,检查 initramfs 或根分区

ps 不可用时,用 printkRun ... as init process 确认 exec 目标。


附录:源码索引

符号 约行 main.c
rest_init 687
kernel_init 1713
kernel_init_freeable 1803
run_init_process 1626

相关03 initcall04 挂根

文章互动

阅读 --

留言

0 条留言

正在加载留言…