一. Linux内核启动
上一篇文章简单了解了 Linux内核启动第二阶段,涉及的 start_kernel函数。start_kernel 函数最后调用了 rest_init 函数,接下来简单看一下 rest_init 函数。
本文续上一篇文章的学习,地址如下:
Linux内核启动流程-第二阶段start_kernel 函数_凌肖战的博客-CSDN博客
二. Linux内核启动流程第二阶段
1. rest_init 函数
rest_init
函数定义在文件
init/main.c
中,函数内容如下:
383 static noinline void __init_refok rest_init(void)
384 {
385 int pid;
386
387 rcu_scheduler_starting();
388 smpboot_thread_init();
389 /*
390 * We need to spawn init first so that it obtains pid 1, however
391 * the init task will end up wanting to create kthreads, which,
392 * if we schedule it before we create kthreadd, will OOPS.
393 */
394 kernel_thread(kernel_init, NULL, CLONE_FS);
395 numa_default_policy();
396 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
397 rcu_read_lock();
398 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
399 rcu_read_unlock();
400 complete(&kthreadd_done);
401
402 /*
403 * The boot idle thread must execute schedule()
404 * at least once to get things moving:
405 */
406 init_idle_bootup_task(current);
407 schedule_preempt_disabled();
408 /* Call into cpu_idle with preempt disabled */
409 cpu_startup_entry(CPUHP_ONLINE);
410 }
第
387
行,调用函数
rcu_scheduler_starting
,启动
RCU
锁调度器。
第
394
行,调用函数
kernel_thread
创建
kernel_init
进程,也就是大名鼎鼎的
init
内核进程。
init
进程的
PID
为
1
。
init
进程一开始是内核进程
(
也就是运行在内核态
)
,后面
init
进程会在根
文件系统中查找名为“
init
”这个程序,这个“
init
”程序处于用户态,通过运行这个“
init
”程
序,
init
进程就会实现从内核态到用户态的转变。
第
396
行,调用函数
kernel_thread
创建
kthreadd
内核进程,此内核进程的
PID
为
2
。
kthreadd进程负责所有内核进程的调度和管理。
第
409
行,最后调用函数
cpu_startup_entry
来进入
idle
进程,
cpu_startup_entry
会调用cpu_idle_loop
,
cpu_idle_loop
是个
while
循环,也就是
idle
进程代码。
idle
进程的
PID
为
0
,
idle
进程叫做空闲进程。
idle
空闲进程
,当
CPU
没有事情做的时候就在
idle
空闲进程里面“瞎逛游”,反正就是给
CPU
找点事做。当其他进程要工作的时候就会抢占
idle
进程,从而夺取
CPU
使用权。其实大
家应该可看到
idle
进程并没有使用
kernel_thread
或者
fork
函数来创建,因为它是有主进程演
变而来的。
开发板上电后,输入“ps -A” 命令 就可以打印出当前系统中的所有进程,其中就能看到 init 进程和 kthreadd 进程,如下图所示:
可以看出,
init
进程的
PID
为
1
,
kthreadd
进程的
PID
为
2
。之所以上图
中没有显示
PID
为
0
的
idle
进程,那是因为
idle
进程是内核进程。
我们接下来重点看一下 init 进程,kernel_init 就是 init 进程的进程函数。