——从BootROM到用户应用,别再把启动流程看成一串黑盒日志
如果你第一次接触嵌入式 Linux,大概率见过这种场景:板子一上电,串口终端里哗啦啦刷一堆日志,先是 U-Boot,然后是 kernel,然后是各种驱动打印,最后出来一个 login 或者 shell。
你看着它启动成功了,好像也挺开心。但真让你解释“刚才到底发生了什么”,很多人就开始含糊:
BootROM 是谁?
U-Boot 为什么要存在?
内核是从哪加载的?
设备树是什么时候用上的?
RootFS 又是什么时候挂载的?
这些东西如果没连起来,后面你学设备树、驱动、文件系统、启动优化、系统裁剪,都会像在看一堆散点。
所以这篇不讲某个具体芯片的启动寄存器,也不钻 U-Boot 源码。我们先把嵌入式 Linux 上电启动的主线概念理清楚。
一块 Linux 板子从上电到跑应用,本质上是在一步步把“裸硬件”变成“可运行应用的系统环境”。
一、先看全流程
一块典型嵌入式 Linux 板子,上电以后大概会走这条链路:
后面所有启动问题,基本都能挂回这条链路上。
板子没反应,可能卡在 BootROM 或 Bootloader;内核起不来,可能是 kernel、设备树、启动参数问题;内核起来但进不了系统,可能是 RootFS 挂载失败;系统起来但应用没跑,可能是 init 脚本或 systemd 服务问题。
也就是说,启动流程不是一坨日志,而是一条有阶段、有交接的链路。
二、第一站:BootROM,芯片上电后的第一口气
BootROM 是芯片内部固化的一小段代码。它不是你编译出来烧进去的,也不是 Linux 的一部分,而是芯片厂商在芯片里预置好的启动代码。
它的任务很朴素:芯片刚上电时,CPU 总得从某个地方开始执行第一条指令。这个“第一个地方”,通常就是 BootROM。
BootROM 会根据芯片的启动配置,决定从哪里找下一阶段程序。比如:
你可以把 BootROM 理解成门卫。板子一上电,它先醒,看看启动拨码、熔丝位或引脚配置,然后决定“我该从哪里把下一段程序请进来”。
这也是为什么有些板子能从 SD 卡启动,有些能从 eMMC 启动,有些短接某个引脚就进入烧录模式。这些都和 BootROM 的启动策略有关。
三、第二站:Bootloader,最常见的是U-Boot
BootROM 能做的事情通常很有限。它不适合直接把 Linux 内核完整拉起来,所以它一般会加载一个更强的启动程序,也就是 Bootloader。
在嵌入式 Linux 里,最常见的 Bootloader 是 U-Boot。
U-Boot 主要干几件事:
| |
|---|
| |
| |
| 从 Flash、eMMC、SD、网络等位置读取 kernel |
| |
| 比如 rootfs 在哪里、console 用哪个串口 |
| |
这里最关键的是 DDR 初始化。Linux 内核运行起来需要比较大的内存,BootROM 阶段通常还没把 DDR 完整配置好,所以 U-Boot 要先把这件事搞定。
换个比喻,BootROM 像门卫,U-Boot 就像开工前的现场负责人。它先把水电、仓库、通道准备好,再把真正干活的队伍,也就是 Linux 内核,请进场。
四、第三站:Kernel,真正的操作系统核心开始接管
U-Boot 把内核加载到内存后,会把控制权交给 Linux Kernel。到这里,系统才开始真正进入 Linux 世界。
Kernel 启动时会做很多事,比如:
这些听起来很多,但你可以先抓住主线:
内核启动的过程,就是 Linux 开始接管硬件和系统资源的过程。
在 RTOS 里,你可能自己写外设初始化、任务创建、驱动初始化。到了 Linux,很多系统级初始化由内核完成,而且它不是按你业务代码的顺序跑,而是按内核框架和驱动模型来组织。
这也是为什么看 Linux 启动日志时,你会看到各种驱动、总线、文件系统、网络模块陆续打印。它们不是乱刷,而是在内核接管系统的过程中逐步被初始化。
五、设备树什么时候登场?
设备树,也就是 Device Tree,是嵌入式 Linux 里非常关键的东西。它通常由 U-Boot 加载,然后传给内核。
那设备树到底干嘛?
设备树负责告诉内核:这块板子的硬件长什么样。
比如:
为什么不直接写死在驱动里?
因为同一个芯片可以做很多块不同的板子。UART 可能接到不同引脚,I2C 上挂的设备可能不同,屏幕、网口、按键、电源管理芯片都可能换。
如果这些都写死在驱动里,每换一块板就改驱动,那驱动会非常痛苦。
设备树的价值就是把“硬件描述”和“驱动逻辑”分开:
所以学习 Linux 驱动时,会经常遇到 compatible、reg、interrupts 这些字段。它们不是为了折磨你,而是在帮内核完成“设备和驱动怎么匹配”这件事。
六、RootFS:内核启动后,总得有个家
内核起来以后,还不等于系统能用了。
它还需要挂载根文件系统,也就是 RootFS。
RootFS 里面有什么?
一般包括:
如果说 Kernel 是系统的大脑,那 RootFS 就像系统的生活区。没有 RootFS,内核可能已经启动了,但它找不到用户态程序,也就没法真正进入可用状态。
很多启动失败都会卡在这里,比如:
VFS: Unable to mount root fs
所以看到板子“内核起来了但进不了系统”,别急着怀疑应用,先看 RootFS 有没有挂上。
七、init/systemd:用户空间的第一棒
RootFS 挂载成功以后,内核会启动用户空间的第一个进程。传统上这个进程叫 init,很多现代 Linux 系统会用 systemd。
这个进程很关键,因为它是用户空间的起点。
它负责继续拉起系统服务,比如:
所以如果系统能进内核,但应用没跑,问题可能不在内核,而在 init 脚本、systemd service、启动顺序、环境变量、权限这些用户空间问题上。
这就是 Linux 和 RTOS 很不一样的地方。
RTOS 里你写任务创建,系统上来就按你的固件逻辑跑。Linux 里,内核先把系统环境搭起来,然后由用户空间启动各种服务和应用。
八、启动问题怎么定位
理解这条链路以后,启动问题就不再是一坨日志了。
你可以按阶段判断:
这张表比死背启动日志有用。
因为你知道每一段是谁交给谁,哪里没交接成功,就顺着那一段查。
九、最后总结一下
嵌入式 Linux 上电启动,不是一口气从硬件跳到应用。
它是一棒接一棒:
BootROM 找到 Bootloader,Bootloader 准备环境并加载 Kernel,Kernel 接管系统并挂载 RootFS,init/systemd 拉起用户空间,最后你的应用才开始跑。
可以把它记成这条主线:
上电 -> BootROM -> U-Boot -> Kernel + Device Tree -> RootFS -> init/systemd -> App
这条线一旦立住,再想想你学习的 U-Boot、设备树、驱动、RootFS、Buildroot,就不会觉得它们是散的。
相关文章
你第一次看 Linux 启动日志时,最懵的是 U-Boot、设备树,还是 RootFS 挂载失败?