一. 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 进程的进程函数。