内核启动阶段kernel_init(init)进程分析

本文详细解析了Linux内核启动阶段的关键步骤,包括启动kernel_init进程、打开控制台设备、挂载根文件系统及启动init进程的过程。揭示了如何通过一系列初始化操作使操作系统能够顺利运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核启动阶段kernel_init(init)进程分析

在kernel进入c语言阶段后,会开始执行start_kernel函数,它负责进行kernel正式运行前各个功能的初始化:打印了一些信息、内核工作需要的模块的初始化被依次调用(譬如内存管理、调度系统、异常处理···),最后末尾调用了一个rest_init函数启动了三个进程(idle、kernel_init、kthreadd),来开启操作系统的正式运行。如下图所示:
这里写图片描述

  • idle是操作系统的空闲进程,当cpu空闲的时候会去运行它
  • kernel_init最开始只是一个函数,这个函数作为进程被启动,但是之后它将读取根文件系统下的init程序,这个操作将完成从内核态到用户态的转变,而这个init进程是所有用户态进程的父进程,它生了大量的子进程,所以init进程将永远存在,其PID是1
  • kthreadd是内核守护进程,其PID为2

接下来就开始分析kernel_init进程(函数)

1.打开控制台设备

    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        printk(KERN_WARNING "Warning: unable to open an initial console.\n");

    (void) sys_dup(0);
    (void) sys_dup(0);
  • 首先打开了/dev/console文件,在Linux中一切皆是文件,所以/dev/console文件代表的是控制台设备(其实就是串口)
  • 在Linux中,进程打开文件后会获得该文件的文件描述符。这里kernel_init进程就得到了控制台的文件描述符,然后又使用sys_dup复制了2次….一共得到了3个文件描述符。这三个文件描述符分别是0、1、2.这三个文件描述符就是所谓的:标准输入、标准输出、标准错误
  • 之后kernel_init所有的子进程都将继承这3个文件描述符,也就是说后面所有的进程一生出来,就默认拥有标准输入、标准输出、标准错误的文件描述符
  • 其实这一步的根本目的就是传承这3个文件描述符、

2.挂载根文件系统

    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }
  • prepare_namespace这个函数负责挂载根文件系统
  • 如果内核挂载根文件系统成功,则会打印出:VFS: Mounted root (xxxx filesystem) on device xxxx.
    如果挂载根文件系统失败,则会打印:No filesystem could mount root, tried: xxxx
  • 一般来说,挂载根文件系统失败的原因有:1.U-boot的环境变量bootargs设置不对,导致传递了错误的参数给kernel;2.还有一定几率是烧录根文件系统的时候出错,这个纯粹是运气问题;3.根文件系统本身制作有问题

3.启动init进程

    init_post();
  • 调用了init_post来启动init进程,这个函数主要内容为
    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %s\n",
                ramdisk_execute_command);
    }

    /*
     * 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.
     */
    if (execute_command) {
        run_init_process(execute_command);
        printk(KERN_WARNING "Failed to execute %s.  Attempting "
                    "defaults...\n", execute_command);
    }
    run_init_process("/sbin/init");
    run_init_process("/etc/init");
    run_init_process("/bin/init");
    run_init_process("/bin/sh");

    panic("No init found.  Try passing init= option to kernel. "
          "See Linux Documentation/init.txt for guidance.");
  • 这个函数会去根文件系统中寻找init程序,具体路径由U-boot的环境变量bootargs提供,一旦init程序被找到,就会启动init进程(该可执行程序的文件名不一定叫init),然后操作系统正式运行,至此一去不复返
  • 如果U-boot的环境变量bootargs没有传过来路径,或者路径中找不到,或者执行出错,那么kernel还留了一手以防万一。init_post函数尾部有四个run_init_process函数,它们会去4个地方看看有没有init程序,如果以上都不成功,就启动失败了
  • 其实这个init程序一般来说是根文件目录下的linuxrc,linuxrc是一个符号链接,指向了busybox。说到底init进程就是busybox
<think>好的,我需要回答用户关于Linux内核初始化中`kernel_init_done`的含义和作用的问题。首先,我需要回忆一下Linux内核启动过程的相关知识。 用户提供的引用中提到了`kernel_init_freeable`函数,其中调用了`wait_for_completion(&kthreadd_done)`,这说明在内核初始化过程中,完成量(completion)机制被用来同步不同阶段的执行。`kthreadd_done`可能表示kthreadd线程已经完成初始化,之后`kernel_init_freeable`才能继续执行。 现在用户问的是`kernel_init_done`,虽然没有在提供的引用中直接提到,但根据命名惯例,`kernel_init_done`可能是一个完成量,用于表示内核初始化阶段的完成。通常,这种完成量会被其他子系统或模块用来等待内核初始化完成后再执行自己的任务。 接下来,我需要确认`kernel_init_done`的实际存在和用途。根据Linux内核代码的结构,完成量通常使用`struct completion`类型,并通过`init_completion()`初始化,`complete()`触发完成,`wait_for_completion()`等待完成。例如,`kernel_init`函数在完成初始化后可能会调用`complete(&kernel_init_done)`,而需要等待该事件的模块会调用`wait_for_completion(&kernel_init_done)`。 此外,用户提供的引用[2]提到了`initcall_run_list`函数,该函数负责执行初始化函数列表。这可能是在内核初始化的后期阶段,当所有初始化函数执行完毕后,触发`kernel_init_done`完成量,通知其他部分内核初始化已完成。 总结来说,`kernel_init_done`的作用是作为内核初始化完成的信号机制,确保依赖内核初始化的模块或进程在继续执行前等待这一阶段的完成。这有助于同步和避免竞态条件,确保系统正确启动。</think>### Kernel_init_done 的定义及作用 在 Linux 内核初始化过程中,`kernel_init_done` 是一个完成量(Completion)机制,用于**同步内核初始化阶段的完成状态**。它通常在 `init/main.c` 中定义,属于内核启动流程中的关键同步点。 #### 1. 定义与初始化 `kernel_init_done` 的定义通常如下(伪代码): ```c static struct completion kernel_init_done; ``` 在内核初始化函数(如 `kernel_init`)的早期阶段,会通过 `init_completion()` 进行初始化: ```c init_completion(&kernel_init_done); ``` #### 2. 作用与触发时机 - **作用**: 标记内核初始化完成,通知依赖此状态的子系统或模块可以继续执行。例如: - 用户空间初始化(如 `systemd` 或 `init` 进程)可能需要等待内核初始化完成后再启动。 - 某些驱动或内核线程需确保内核核心功能已就绪。 - **触发时机**: 在 `kernel_init` 函数的末尾(所有关键初始化完成后)调用: ```c complete(&kernel_init_done); ``` #### 3. 使用场景示例 其他模块可通过 `wait_for_completion()` 等待该事件: ```c wait_for_completion(&kernel_init_done); ``` 例如,用户空间初始化进程可能通过此机制确保内核已准备好环境[^1][^2]。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值