嵌入式 Linux 启动流程详解 (以 ARM + U-Boot 为例)

嵌入式 Linux 启动流程详解 (以 ARM + U-Boot 为例)

对于嵌入式开发者而言,深入理解系统的启动流程至关重要。这不仅有助于进行底层驱动开发和系统移植,还能在遇到启动失败等问题时,快速定位和解决。本文将详细分解基于 ARM 架构的嵌入式 Linux 系统最常见的启动流程:BootROM -> U-Boot -> Kernel -> RootFS。过段时间仔细研究一下Uboot,看看能不能给手上这块3506移植个Uboot试试。


启动流程总览

嵌入式设备的启动过程是一个环环相扣的链条,前一级加载器负责初始化基础硬件,并加载下一级更复杂的程序,直到最终启动完整的 Linux 操作系统。

graph TD subgraph SoC 内部芯片 A[电源开启] --> B(BootROM); end subgraph 引导加载程序 (Bootloader) B --> |从 Flash/SD卡 加载| C[U-Boot SPL]; C --> |初始化DDR| D[U-Boot 主体]; end subgraph 内核与用户空间 D --> |执行 bootcmd| E[加载 Linux 内核 (zImage)]; D --> |执行 bootcmd| F[加载设备树 (DTB)]; E & F --> G{启动内核}; G --> H[内核初始化硬件]; H --> I[挂载根文件系统 (RootFS)]; I --> J[执行第一个用户进程 /sbin/init]; J --> K[启动系统服务与应用]; end

1. BootROM - 芯片内的第一行代码

BootROM 是整个启动链的起点。

  • 是什么 :一段固化在 SoC (System on Chip) 芯片内部的只读存储器 (ROM) 中的程序。它由芯片制造商编写,用户无法修改,是上电后 CPU 执行的第一段代码
  • 核心职责
    1. 最小化硬件初始化:进行最基础的硬件设置,如配置内部时钟、初始化用于加载下一阶段代码的存储接口(如 SD 卡控制器、eMMC、SPI Flash 等)。
    2. 加载外部引导程序 :根据预设的启动介质顺序(通常由一组 BOOT_MODE 引脚的电平状态决定),从外部存储设备(Flash, eMMC, SD Card)中查找并加载第二阶段的引导加载程序(通常是 U-Boot 的一个小型版本 SPL)到芯片内部的 SRAM 中。
    3. 移交控制权:加载完成后,将 CPU 的执行权限交给这段刚刚加载进来的代码。

BootROM 的工作非常单一,就是为更强大的 Bootloader 搭建一个最小化的运行环境。


2. U-Boot - 通用引导加载程序

U-Boot (Universal Boot Loader) 是嵌入式领域应用最广泛的开源引导加载程序。它功能强大,通常分两个阶段执行,以适应嵌入式系统 SRAM 较小的限制。

2.1 第一阶段: U-Boot SPL (Secondary Program Loader)

  • SPL 是什么:一个精简版的 U-Boot,主要目的是初始化 DDR 内存。由于 BootROM 加载 SPL 时使用的是芯片内部的 SRAM,而 SRAM 通常很小(几十到几百 KB),无法容纳完整的 U-Boot。
  • 核心职责
    1. 初始化 DDR 控制器:这是 SPL 最重要的任务。DDR 内存是运行 Linux 内核和应用程序所必需的大容量内存空间。
    2. 加载 U-Boot 主体 :DDR 初始化完成后,SPL 会从外部存储设备中将完整的 U-Boot 镜像 (u-boot.img) 加载到 DDR 内存中。
    3. 跳转执行:将控制权移交给位于 DDR 中的 U-Boot 主体。

2.2 第二阶段: U-Boot 主体

这是我们通常所说的 U-Boot,它功能完善,为启动 Linux 内核提供了所有必要的准备。

  • 核心职责
    1. 全面的硬件初始化:初始化系统所需的各种外设,如串口(用于打印启动日志和提供命令行交互)、网络接口(用于网络启动和调试)、存储设备(eMMC, NAND)等。
    2. 设置内核启动参数 (bootargs) :这是 U-Boot 的一个关键功能。它会设置一个名为 bootargs 的环境变量,然后将其传递给 Linux 内核。这个参数字符串告诉内核一些重要的初始配置,例如:
      • console=ttyS0,115200:指定内核使用哪个串口作为控制台,以及波特率。
      • root=/dev/mmcblk0p2 rootwait:指定根文件系统的位置(例如在 SD 卡的第 2 个分区),并让内核等待设备就绪。
      • rootfstype=ext4:指定根文件系统的类型。
    3. 加载内核和设备树 :根据 bootcmd 环境变量中定义的命令(或手动输入),从存储介质(或通过网络 TFTP)中将 Linux 内核镜像 (zImage) 和设备树文件 (.dtb) 加载到 DDR 内存的指定地址。
    4. 启动内核 :执行 bootzbootm 命令,将设备树文件的内存地址传递给内核,然后跳转到内核的入口点,正式开始 Linux 系统的启动。

3. Linux Kernel - 操作系统的核心

内核接管控制权后,标志着真正的操作系统开始运行。

  1. 内核自解压zImage 是一个压缩的镜像,所以第一步是在内存中将自己解压出来。
  2. 解析设备树 (DTB):内核会读取 U-Boot 传递过来的 DTB 文件。DTB 描述了板级的所有硬件信息,内核根据这些信息来初始化对应的驱动程序。这使得内核代码可以与硬件解耦,一份内核源码可以适配不同的开发板。
  3. 初始化硬件:内核开始全面初始化由 DTB 描述的所有硬件设备,并建立起驱动模型、内存管理、进程调度等核心子系统。
  4. 挂载根文件系统 (RootFS) :内核根据 bootargs 中的 root 参数,找到并挂载根文件系统。
    • 在开发阶段,常常使用 NFS (Network File System),方便快速修改和调试。
    • 在量产产品中,根文件系统通常存放在 eMMC 或 Flash 的一个分区上,格式为 ext4, squashfs (只读压缩), ubifs (用于 NAND Flash) 等。
  5. 启动 init 进程 :根文件系统被挂载后,内核会创建并运行第一个用户空间的进程 /sbin/init(其 PID 永远为 1)。内核的初始化工作到此结束,后续的用户空间初始化全权交由 init 进程处理。

4. Init 与用户空间

init 进程是所有用户空间进程的"始祖"。它的任务是根据配置文件,将系统带入一个可用的状态。

  • 常见的 init 程序 :
    • BusyBox init :在资源受限的嵌入式系统中非常流行。它解析 /etc/inittab 文件,按顺序执行其中的脚本来启动系统服务。
    • SystemV init :传统的 init 系统,同样使用 /etc/inittab 和运行级别 (Runlevel) 脚本 (/etc/rc.d/)。
    • systemd:现代 Linux 发行版的主流选择,功能强大,并行启动速度快,但在嵌入式领域有时被认为过于复杂和庞大。
  • 执行流程init 进程会运行一系列启动脚本,这些脚本会:
    • 挂载其他必要的文件系统(如 /proc, /sys, /tmp)。
    • 配置网络。
    • 启动系统日志、SSH 服务等后台守护进程。
    • 最终,启动核心的应用程序

当用户的应用程序成功运行起来后,整个嵌入式 Linux 系统的启动流程便宣告完成。

相关推荐
一枝小雨1 分钟前
STM32中的Flash、ROM与RAM全解析
stm32·单片机·嵌入式·arm·内存分布
Mr.456718 分钟前
Linux&Windows环境下Nacos3.1.0详细安装配置指南:从零到生产就绪
linux·运维·服务器
要做朋鱼燕42 分钟前
ARM CoreSight:多核SoC调试追踪架构解析
开发语言·笔记·职场和发展·嵌入式·嵌入式软件
国科安芯1 小时前
AS32S601ZIT2型MCU:基于RISC-V架构的抗辐照设计与试验评估
网络·单片机·嵌入式硬件·fpga开发·架构·硬件架构·risc-v
峰顶听歌的鲸鱼1 小时前
30.Linux DHCP 服务器
linux·运维·服务器·笔记·学习方法
Lzc7741 小时前
Linux的网络基础
linux·linux的网络基础
violet-lz2 小时前
Linux文件系统调用:文件调用函数与exec系统函数详解与应用
linux·运维·服务器
La Pulga2 小时前
【STM32】ADC数模转换器
c语言·stm32·单片机·嵌入式硬件·mcu
充哥单片机设计2 小时前
【STM32项目开源】基于STM32的智能点滴输液系统
stm32·单片机·嵌入式硬件
袁泽斌的学习记录3 小时前
ubuntu22.04安装cuda11.4版本
linux·运维·服务器