linux 启动流程?

linux 启动流程

CPU 上电后最先执行的启动代码,通常确实是放在 arch 目录下对应架构的启动文件里。这是因为启动代码强相关于 CPU 架构和硬件细节,不同架构差异非常大。


具体说明

1. 为什么启动代码放在 arch 目录?

  • 启动代码要设置 CPU 状态(如寄存器、MMU、异常向量表等)。
  • 各架构 CPU 启动流程不一样,汇编语言代码也差别大。
  • 所以放在 arch/架构名/boot/ 或类似目录,便于管理。

2. 以常见架构为例

架构 启动代码位置示例 说明
x86 arch/x86/boot/ 包含 real mode 到 protected mode 切换等代码
ARM arch/arm/boot/ 包含内核入口汇编、initrd搬移等代码
ARM64 arch/arm64/kernel/head.S 64位 ARM 启动汇编代码
MIPS arch/mips/kernel/ MIPS 启动相关代码

3. 启动流程简述

  • CPU 上电后,会跳转到一个固定的物理地址(Reset Vector)。
  • 该地址对应的代码就是架构相关的启动代码,一般就是 arch/.../boot/... 里的汇编。
  • 启动代码初始化最基本的硬件,设置内存和CPU状态。
  • 然后跳转进入 C 语言编写的内核入口函数 start_kernel()。

总结

  • CPU 上电后的第一段执行代码一定是架构相关的启动代码,放在 arch 目录下。
  • 这是因为启动时的硬件初始化和上下文设置紧密依赖 CPU 架构。
  • 通用内核代码则放在 kernel/、mm/、fs/ 等目录。

核心原因是内核启动前后环境差异巨大,必须用汇编代码做早期硬件和处理器环境初始化,才能安全可靠地进入 start_kernel。

Linux 内核源码里的 arch/ 目录是 不同 CPU 架构相关代码的汇总目录。


具体解释

1. Linux 内核支持多种 CPU 架构

  • 比如 x86、ARM、MIPS、PowerPC、RISC-V 等。
  • 每种架构底层硬件接口和启动流程差别很大。
  • 因此,内核源码会根据架构分目录管理。

2. arch/ 目录的作用

  • arch/ 目录下每个子目录对应一个 CPU 架构,比如:

    arch/
    ├── x86/ # x86 架构相关代码
    ├── arm/ # 32位 ARM 架构代码
    ├── arm64/ # 64位 ARM 架构代码
    ├── mips/ # MIPS 架构代码
    ├── riscv/ # RISC-V 架构代码
    ...

  • 每个目录里包含启动汇编、CPU相关驱动、体系结构专属的调度、内存管理、异常处理等代码。

  • 其他跟架构无关的内核通用代码则在 kernel/、mm/、fs/ 等目录。


详细解释

1. CPU 上电后环境非常"原始"

  • CPU 刚上电时,只处于物理地址模式,没有开启分页(虚拟内存)
  • 寄存器值不确定
  • 堆栈、内存布局未初始化
  • 特权级和CPU模式尚未设置

此时无法直接跳转执行复杂的C代码,因为C代码依赖于正确的栈、数据段和运行环境。


2. 汇编入口(_start)做的事情

汇编代码位于内核最开始的入口,做以下关键工作:

  • 设置 CPU 特权级(例如切换到内核态)
  • 配置段寄存器、堆栈指针
  • 设置分页机制(启用虚拟内存)
  • 清理未初始化的BSS段
  • 设置全局指针、数据指针等CPU寄存器
  • 关闭中断或做其他硬件相关初始化

这些操作都只能用汇编完成,因为C环境尚未准备好。


3. start_kernel 的前提

start_kernel() 是用 C 语言写的,它假设运行环境已经稳定:

  • 栈已初始化
  • 全局变量可以正常访问
  • CPU 处于正确模式
  • 内存管理子系统已部分就绪

换句话说,start_kernel 是"内核的真正入口",但不是"CPU的入口"。


4. 总结

阶段 作用 为什么不能直接跳到 start_kernel
CPU 上电 → _start 汇编 初始化 CPU 寄存器、栈、分页、特权级 需要底层硬件初始化,C代码依赖这些,直接跳C会崩溃
_start → start_kernel 进入内核 C 语言环境,启动内核子系统 需要完整运行环境,汇编做的准备工作必须先完成

简单比喻

汇编入口相当于"内核启动的引导员",负责把环境布置好;
start_kernel 是"内核真正的司机",环境准备好了才上车启动。


好的,讲讲 secondary_start_kernel 的作用和背景。


什么是 secondary_start_kernel?

  • 它是 Linux 内核在多核(SMP)系统中,非主CPU(Secondary CPU) 启动时的入口函数。
  • 主CPU(CPU0)执行 start_kernel 来启动整个系统和初始化多核环境。
  • 其他CPU启动后,会跳到 secondary_start_kernel,完成自己的初始化流程。

为什么需要 secondary_start_kernel?

  • 多核CPU启动时,只有主核(CPU0)从复位向量启动,执行完整的内核启动流程。
  • 其他核处于停顿状态,需要被唤醒,并单独执行一套简化的初始化。
  • 这些核不会执行 start_kernel,而是执行 secondary_start_kernel,完成自己的CPU初始化和进入调度。

secondary_start_kernel 主要做什么?

  • 初始化该CPU的本地数据结构(如CPU寄存器、TSS、IDT等)
  • 设置该CPU的中断和调度环境
  • 把该CPU加入到内核的调度器中,开始运行任务
  • 不重复执行主核完成的系统全局初始化

简单流程示意

复制代码
主核 (CPU0) 启动 -> 执行 start_kernel -> 初始化系统 -> 唤醒其他核
其他核 (CPU1..N) 启动 -> 执行 secondary_start_kernel -> 初始化本地CPU环境 -> 进入调度循环

位置与调用

  • secondary_start_kernel 通常定义在 init/main.c
  • 各架构启动汇编在唤醒其他CPU时会跳转到这个函数
  • 是 SMP 初始化的关键环节

总结

函数名 作用 适用CPU
start_kernel 主核启动,系统初始化 CPU0(主CPU)
secondary_start_kernel 非主核启动,局部CPU初始化和进入调度 其他CPU(辅助CPU)

好的,下面详细介绍 initrd 在 ARM 和 x86 Linux 内核启动流程中的具体实现细节,帮助你理解它是怎么工作的。


一、initrd 的启动流程核心概念

  • initrd 是内核启动时内存中的一个临时根文件系统映像。
  • 内核启动时先挂载它作为根文件系统,执行初始化工作,之后切换到真实根文件系统。
  • 在启动参数中通过内核命令行参数或 bootloader 传递 initrd 的位置和大小。

二、x86 启动流程中的 initrd

1. Bootloader 阶段

  • Bootloader(如 GRUB)加载内核镜像 vmlinuz 和 initrd 镜像到内存。
  • 将 initrd 起始地址和大小放入内核启动参数(例如内核命令行中的 initrd=0x地址,大小)。
  • 将控制权交给内核。

2. 内核启动汇编阶段

  • 在 arch/x86/boot/header.S 等文件,内核启动汇编代码初始化后,会调用内核的 C 入口 start_kernel()。
  • 内核解析启动参数,获得 initrd 地址和大小。

3. 内核 C 语言初始化阶段(init/do_mounts.c)

  • 内核根据启动参数调用 setup_initrd()(2.6 内核及类似版本)。
  • setup_initrd() 负责把 initrd 映像注册为临时块设备(ramdisk),并挂载为根文件系统(initrd rootfs)。
  • 调用 initrd 内部的 /init 程序进行系统初始化。

4. 切换到真实根文件系统

  • 初始化完成后,pivot_root() 或 switch_root() 将根文件系统切换到硬盘或其他设备的文件系统。
  • initrd 被卸载,内核开始正常运行用户空间系统。

三、ARM 启动流程中的 initrd

1. Bootloader 阶段

  • Bootloader(如 U-Boot)将内核镜像(zImage/uImage)和 initrd 加载到内存。

  • U-Boot 通过寄存器(如 r0-r3)传递内核启动参数:

    • r2 寄存器通常保存 initrd 的起始地址。
    • r3 寄存器保存 initrd 大小。
  • 设备树(DTB)也可能携带 initrd 信息。

2. ARM 汇编入口阶段 (arch/arm/boot/bootp/init.S 等)

  • 内核启动汇编代码读取这些参数,做内存搬移(前面你看过的 init.S 代码就是搬移 initrd 的例子)。
  • 将 initrd 拷贝到合适内存区域,确保后续内核使用。

3. 内核 C 入口 (start_kernel)

  • 解析传入的 initrd 地址和大小,调用 setup_initrd()。
  • initrd 作为临时根文件系统挂载。
  • 执行 initrd 内部初始化脚本。

4. 切换根文件系统

  • 跟 x86 类似,切换到真实根文件系统,完成启动。

四、内核中与 initrd 相关的关键函数

  • setup_arch():在架构相关初始化时调用,解析启动参数,设置 initrd 信息。
  • setup_initrd():注册并挂载 initrd,准备临时根文件系统。
  • mount_root():挂载根文件系统时优先使用 initrd,后续切换根。
  • pivot_root() / switch_root():实现根文件系统的切换。

五、简要总结流程图

复制代码
Bootloader 加载内核和 initrd --> 内核启动汇编(解析initrd参数,搬移initrd) 
       --> start_kernel(调用setup_initrd,挂载initrd) 
       --> 执行initrd内的/init程序初始化 
       --> 切换到真实根文件系统(硬盘/网络文件系统等)

六、相关文件位置(Linux 源码)

功能 典型源码路径
ARM 启动汇编 arch/arm/boot/bootp/init.S
x86 启动汇编 arch/x86/boot/header.S
initrd 处理 init/do_mounts.c、init/do_mounts.h
根文件系统挂载 fs/namespace.c

当然可以,下面是将你提供的 Linux 启动流程(包括 Bootloader、主核、从核、initrd、根文件系统切换等)融合成的一个 Mermaid 序列图:


✅ Mermaid 序列图(Linux 多核启动流程 + initrd 启动)

Bootloader 主核(CPU0) 其他核(CPU1..N) Linux 内核 initrd(临时根文件系统) 真实根文件系统 上电,跳转内核入口(arch/.../head.S) 执行 start_kernel() 初始化内核子系统(时钟、中断、内存、调度器等) 唤醒其他核(SMP) 执行 secondary_start_kernel() 初始化 CPU 本地环境,进入 idle/sched 循环 解析内核参数,包括 initrd 调用 setup_initrd() 解压并挂载 initrd 执行 initrd 中的 /init 程序 完成系统初始化逻辑(例如挂载 dev tmpfs) 切换到真实根文件系统(硬盘/网络等) 执行 /sbin/init(或 systemd) 系统启动完成,进入用户空间 Bootloader 主核(CPU0) 其他核(CPU1..N) Linux 内核 initrd(临时根文件系统) 真实根文件系统


📌 说明

  • CPU0 是主核,从入口跳入 start_kernel,控制整个启动过程。
  • CPU1..N 是从核,由主核在适当时机唤醒,执行 secondary_start_kernel。
  • Bootloader 通常负责将内核、initrd 以及设备树加载到内存,并设置参数。
  • initrd 是临时根文件系统,内核通过参数挂载它,并执行其中的 /init。
  • /init 会负责进一步初始化和挂载真实根文件系统(如 ext4、NFS 等)。
  • 最终系统进入真实根文件系统的 /sbin/init,开始正常运行。

相关推荐
夜听莺儿鸣1 小时前
502-003_Linux 中断与异常(一):从Linux角度理解中断与异常
linux·中断与异常·linux中断
杨云龙UP2 小时前
TDengine 3.4.2.8 Community 三节点三副本生产集群部署实战(DNode/MNode/taosAdapter/Explorer)
大数据·linux·运维·数据库·tdengine·时序库
向成科技3 小时前
XC3576H工控主板|深度适配Ubuntu 26.04 LTS,释放边缘AI与工业开发新潜能
linux·人工智能·ubuntu·机器人·硬件·主板·边缘ai
Fcy6484 小时前
Linux下 进程间关系与守护进程
linux·运维·服务器·守护进程
码农小韩4 小时前
Linux驱动理论(二)——Linux字符设备驱动
linux·嵌入式软件开发·linux操作系统·linux应用开发·linux驱动理论
well06124 小时前
Linux粘滞位与Makefile机制深度解析
linux·运维·服务器
再写一行代码就下班4 小时前
linux sh脚本在windows修改导致无法使用解决方式
java·linux·centos
额额额对了5 小时前
SPI通信
linux·c语言·汇编·单片机·嵌入式硬件·arm
子木HAPPY阳VIP5 小时前
Ubuntu 关闭防火墙操作步骤
linux·运维·ubuntu
王振超wzc5 小时前
嵌入式开发环境搭建--WM软件安装,Ubuntu操作系统安装
linux·运维·ubuntu