1. 几道题
先看几道题,自测看看是否需要阅读本篇文章。
Q1. 上电后 DRAM 尚未初始化时,通常由谁运行?
A. 完整 U-Boot(在 DDR 中)
B. BootROM / SPL(片内 SRAM 路径)
C. Linux 内核早期解压代码
D. systemd 的 generator
答案:B。早期只能在 SRAM/ROM 路径,SPL 负责把 DDR 拉起来再加载 U-Boot proper。
Q2. U-Boot 与 Linux 内核之间,最核心的「硬件描述」契约是?
A. /etc/fstab
B. systemd target
C. Device Tree Blob (DTB) + bootargs
D. glibc ABI
答案:C。DTB 描述板级硬件,bootargs 传递 root/console 等运行参数。
Q3. 「业务上电后要延迟 5 秒再连云」这类策略,优先放在哪一层?
A. 用户态服务 / 应用(如 systemd timer 或业务逻辑)
B. 内核调度器源码
C. BootROM
D. 设备树 clock 节点
答案:A。产品策略应留在用户态,便于迭代且不影响内核稳定性。
Q4. 串口停在 “Starting kernel …” 之后没有继续,优先怀疑?
A. systemd 某个 unit 挂了
B. 应用层 segfault
C. D-Bus 未启动
D. 内核早期启动 / 控制台 / DTB 或解压跳转问题
答案:D。此时 PID 1 尚未运行,应查内核早期与 U-Boot 交接(地址、DTB、console)。
2. 软件是怎么“叠”在硬件上的
嵌入式Linux从来不是“一个大程序”,而是一条多阶段接力的启动链,加上运行期稳定存在的内核空间 + 用户空间。

这张图有两个时间维度,千万不要混为一谈:
- 启动期(一次性):BootROM → SPL → U-Boot → Kernel → 挂载rootfs → 启动init
- 运行期(长期):内核 + rootfs上的进程树;应用通过系统调用与驱动交互
3. 五层职责
3.1 BootROM —— 我们改不了的那一层
BootROM固化在i.MX6ULL硅片里,出厂即定。它根据启动引脚/熔丝选择启动介质(SD、eMMC、NAND、USB下载等),把第一级可执行镜像载入片内SRAM。
我们不需要动它,但必须知道它的存在。
3.2 SPL + U-Boot —— 把DDR“扶起来”的接力手
为什么要分SPL和完整U-Boot?因为上电瞬间DRAM还不可用,完整U-Boot放不进片内SRAM。
- SPL:U-Boot的精简第一级,运行于片内SRAM,负责把DDR调起来
- U-Boot:完整bootloader,提供命令行、环境变量、从存储/网络加载内核镜像与DTB
这一层的核心职责是准备“可启动的内存布局”:
bootargs是U-Boot传给Linux的命令行字符串,典型内容如console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait。它是启动层与内核层之间的重要契约。
3.3 Linux内核 —— 资源管理者与硬件抽象中枢
内核我们先记住四大块就够用了:
Linux 内核四大块内核不该塞业务策略(比如“三次重试后弹窗提示用户”)。那是用户态的事。内核只做三件事:接口稳定、资源安全、策略可配置。
3.4 根文件系统 —— 内核启动后的“世界”
根文件系统是内核mount成功后的完整世界:动态库、配置、/dev、/etc、服务单元、应用二进制。
没有可用的rootfs,内核会直接panic——经典报错:
VFS: Unable to mount root fs on unknown-block(0,0)
Rootfs可由eMMC/SD分区、NFS、initramfs等承载。Buildroot/Yocto的核心产物之一就是它。
3.5 用户态与PID 1 —— 产品的“灵魂”
内核启动的第一个用户进程是PID 1,也就是init。
我们接触的systemd就是init的一种实现:它负责服务依赖图、日志、目标运行态。在它之上,才是我们的采集、UI、云连接、OTA等业务进程。
PID 1如果退出,内核通常会panic或触发重启策略。它比普通进程“高贵”得多。
4. 层与层之间的“契约接口”
架构清晰,往往因为接口比实现更稳定。记住四条跨层契约:
四条跨层契约接口工作落地一句话:改需求时先问“我改的是哪条契约的哪一侧?”
改契约本身(比如换root设备命名、改DTB兼容字符串)比改内部实现更危险,必须回归启动与量产流程。
5. i.MX6ULL上实操
U-Boot层
- 打断启动,执行
printenv,找bootargs、bootcmd、fdt_file/fdtfile、loadaddr - 典型加载地址(NXP 6UL/6ULL常见):kernel ≈
0x80800000,DTB ≈ 0x83000000 - 跳转命令常见:
bootz ${loadaddr} - ${fdt_addr}(中间的-表示无initramfs)
Linux内核层
cat /proc/cmdline —— 看到U-Boot传入的bootargsls /sys/firmware/devicetree/base —— 查看运行中的设备树
用户态层
ps -p 1 -o comm= —— 确认PID 1是systemd还是其他systemctl status —— 看服务依赖图