0%

Linux initramfs分析

代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* 从内核的c代码入口看起 */
start_kernel
/* 这个函数已经接近start_kernel末尾,但是内容却无比丰富 */
+-> arch_call_rest_init
/* 在这个函数里拉起1号进程和2号线程 */
+-> rest_init
/*
* kernel_init是1号进程对应的函数,这个函数有点特殊,这个函数除了最终
* 拉起第一个用户态进程外,还做了一堆初始化操作。
*/
|
--+-> user_mode_thread(kernel_init, NULL, CLONE_FS)
|
+-> kernel_init
/* 进来啥也不干,先等2号线程起来 */
wait_for_complete(...)
/*
* 继续初始化一堆东西: smp, smp schedule, work queue, 各种驱动的初始化,
* 各种__init函数的调用,跑内核自带的各种测试,等待initramfs解压完成,
* 其中最后一个和本文的主题相关,在这之前的do_basic_setup()里会开始
* 做initramfs/initrd的解压。
*/
+-> kernel_init_freeable
+-> do_basic_setup
/*
* 这是一个rootfs_initcall修饰过的函数,在do_basic_setup依次调用
* 各种类型的__init函数的时候,会调用到。这个函数的本质是把do_populate_rootfs
* 放到一个workqueue里去执行。
*
* 对于rootfs.cpio.gz的压缩过的内存文件系统,这个函数里会做相关
* 的解压缩操作。
*/
+-> populate_rootfs
+-> async_schedule_domain(do_populate_rootfs, ...)
+-> wait_for_initramfs

| /* 创建threadd这个内核线程,这个线程是所有内核线程的父线程,他就是2号线程 */
--+-> kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES)
|

+-> cpu_startup_entry()

总结下如上的线程模型,内核执行到这里会拉起1号和2号进程,这个时候会出现3个进程并发
的场景,这三个进程分别是0/1/2号进程。kernel_init会等2号线程启动后才会继续跑。可见
initramfs的解压缩是被放到内核线程里去跑的,1号线程会异步的等initramfs解压缩完后
再继续跑。

1号线程等解压完成在wait_for_initramfs里,这个函数用了wait_event,这个函数wait
在D状态(TASK_UNINTERRUPTIBLE),这个状态的线程只能被特定事件唤醒,内核里会定时检查
D状态的线程,如果超过一定事件没有被唤醒(一般是120s),就会报异常。所以,如果解压
过程超过120s,会导致等待时间超过120s,进而报异常(linux/kernel/hung_task.c)。