← 返回嵌入式学习资料第 13 章 · 嵌入式 Linux

CHAPTER 13 · EMBEDDED LINUX

嵌入式 Linux
让系统可以交接

从 ROM、U‑Boot、设备树和内核开始,走到 rootfs、用户空间、存储安全、调试、性能与长期交付;把主机可复现路径、QEMU 边界和真实板卡的启动、驱动、供电、Flash 与恢复条件明确分开。

6 个一级单元48 个递进小节主机路径可复现

TEXTBOOK MAP

CHAPTER 13 · MAP

从启动交接到可维护系统

六个一级单元对应六种能力:解释启动链、描述硬件、固定构建、治理用户空间、保护存储、完成调试与交付。

先把系统交接读懂
  • 01 ROM、U‑Boot、内核与 PID 1
  • 02 设备树、资源与驱动
再把系统做成产品
  • 03 工具链、Buildroot 与 Yocto
  • 04 服务、IPC、权限与日志
最后把系统交给现场
  • 05 分区、A/B、签名与恢复
  • 06 观测、CI、SBOM 与交付

小节数量按认知任务变化:启动、设备树、构建和交付需要连续证据;每个单元都保留无目标板主机路径,并明确 DDR、Flash、电源、驱动、权限和现场边界。

使用方式:先从左侧选择一个嵌入式 Linux 问题,再按启动链、设备树、构建、用户空间、存储和交付推进。每个单元底部有前后翻页;小节标题直接说明正在追踪的阶段、资源、服务、文件系统或证据。
主机边界:默认使用标准库状态机、清单、哈希、A/B、IPC、日志和预算模型;没有开发板也能完成交接、构建、服务、恢复和发布门禁练习。
目标机边界:真实 SoC ROM、DDR、U‑Boot、内核、DTB、驱动、pinctrl、中断、Flash、供电、温度、签名、QEMU/HIL 和现场恢复必须回到当前板卡、内核/BSP 版本和官方资料。

01 · BOOT CHAIN

启动链:从上电到用户空间

嵌入式 Linux 不是把一个内核文件写进 Flash 就结束。ROM、固件阶段、TF‑A/OP‑TEE(若有)、U‑Boot、设备树、内核、initramfs、真实根文件系统和 PID 1 依次交接;任何一段的地址、格式、时钟、内存或安全策略不匹配,都会在下一段表现为‘黑屏’。本单元建立可观察的启动链。

看懂画出固件、内核、设备树、根文件系统和服务的交接。
说清把启动失败定位到最后一个已证明的阶段。
开始做为串口日志、启动参数和恢复介质写出边界。
官方基线Buildroot User Manual下载、配置、构建、复现和法律信息按 2026.08 手册验收。锁定:2026.08 · 核对日期:2026-09-05

13.1.1 上电、复位与第一条串口

启动链是一个严格的交接协议:前一阶段必须把下一阶段需要的镜像、地址、设备描述、时钟和安全上下文准备好。ROM 的行为由芯片固定,SPL/TF‑A 可能负责 DDR、时钟和安全世界,U‑Boot 负责选择启动方法,Linux 内核负责驱动和内存管理,PID 1 才开始组织用户空间服务。每段都有独立的输入和输出。

13.1.2 SPL/TF‑A 与内存初始化

串口日志要以阶段为单位保存,而不是只复制最后几十行。记录 ROM 是否有输出、SPL 是否完成 DDR、U‑Boot 是否加载了哪个文件、DTB magic/compatible、kernel entry、initramfs 大小、root= 参数和内核是否执行到 `Run /init`。同一块板子的启动速度变化也要带时间戳,避免把偶然变慢当成阶段缺失。

PYTHON · BOOT HANDOFF
from dataclasses import dataclass @dataclass(frozen=True) class Handoff: stage: str artifact: str hash_value: str next_stage: str def is_complete(handoff): return bool(handoff.stage and handoff.artifact and handoff.hash_value and handoff.next_stage)

主机检查启动交接字段;真实 ROM、DDR、串口和安全启动仍需目标板证据。

C · U-BOOT SHAPE
setenv kernel_addr_r 0x42000000 setenv fdt_addr_r 0x43000000 setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait' booti ${kernel_addr_r} - ${fdt_addr_r}

命令只是形状示例;地址、串口、rootfs、DTB 和 U-Boot 版本必须按板卡核对。

13.1.3 U‑Boot 环境与 bootflow

U‑Boot 的环境变量、脚本、bootflow 设备和启动方法可能随版本或发行版变化。一个变量名存在不代表它会被当前 bootcmd 使用;一个 `booti` 成功也不代表 DTB 与内核 ABI 匹配。应保存 `printenv`、启动命令、加载地址、文件哈希和板级配置,才能复查‘到底启动了什么’。

表 1 · 启动链:从上电到用户空间的证据判断表
对象应留下的证据不能直接推出
交接镜像/地址/DTB/bootargsU-Boot 提示符就是 Linux
内核initramfs/root=挂载失败只有一个原因
用户PID 1/readiness进程存在就是服务健康

13.1.4 设备树随内核一起交接

Linux 可以从内置或外置 initramfs 开始,在其中加载驱动、解密、寻找真实根分区,再切换到新的根。`root=/dev/mmcblk0p2`、PARTUUID、NFS 或 UBIFS 的发现条件不同;rootfs 挂载失败可能是介质未就绪、驱动未编入、文件系统损坏、命令行错误或电源复位,不能统称为内核坏了。

13.1.5 内核解压、initramfs 与 root=

PID 1 是用户空间的第一个进程,负责继续挂载、启动服务、处理孤儿进程和响应关机。它可以是 BusyBox init、systemd 或自定义程序,但接口和失败语义不同。`/sbin/init` 存在只证明文件可执行,不证明网络、时钟、日志和设备节点已经准备好;服务依赖要有明确条件。

图 1.1 · 启动链:从上电到用户空间的判断路径每一格都要有自己的条件。
阶段ROM→SPL→U‑Boot
描述DTB/bootargs
内核Image/initramfs
用户rootfs/PID 1

从左到右是思考顺序,不是把启动、设备树、用户空间和交付证据压缩成一个“通过”。

13.1.6 PID 1、控制台与第一个服务

安全启动把镜像签名、版本、密钥和失败动作串起来。签名失败应该停在可信的恢复路径,不能自动运行未知镜像;同时要有串口/USB/网络救援边界、恢复介质、计数器和日志。开发阶段可以保留可调试入口,量产阶段则要记录哪些 eFuse、回滚索引和下载接口发生了不可逆变化。

13.1.7 安全启动与失败回到哪里

启动交接卡至少包含 board、SoC、storage、stage、artifact、hash、load_address、entry、dtb_compatible、bootargs、console、rootfs、pid1、timeout、reset_cause、trust_policy、recovery_media、host_result、target_result 和 unknowns。它让读者在没有同一块板时仍能理解证据链,也让目标机实验知道从哪里停止。

13.1.8 本节交付:启动交接卡

启动失败的最小复盘顺序是:保存上一次能到达的阶段,固定镜像和配置,只改变一个变量,再确认新的最后阶段。若修改 DTB 后日志从内核前消失,先检查加载地址和串口,而不是立刻重编译整个发行版;若能进入 shell 但服务失败,则把问题交给用户空间,而不要反复刷 bootloader。

CHECKPOINT 13.1把“一块板子如何把不可见的启动阶段变成可定位的系统?”写成一份可复查记录

请围绕“启动链:从上电到用户空间”写出板卡与版本、输入和前置条件、启动/驱动/服务/存储机制、输出与完成语义、一个安全或资源约束、一个失败路径和对应证据。没有目标板时,运行主机路径并把未执行字段标为未执行;拥有目标机时,还要补齐 SoC、bootloader、内核、DTB、rootfs、工具链、供电、存储、温度、负载和观察条件。不要用一次启动、一个 probe、一次 QEMU、一个哈希或一行日志覆盖没有做过的验证。

合格记录要让没有现场上下文的读者知道先查什么、先运行什么、看到什么才算通过,以及哪一句结论仍然不能推出。未知项、停止条件、恢复镜像、权限边界、许可证和回滚动作都是交付的一部分。

02 · DEVICETREE AND DRIVERS

设备树与驱动:描述硬件,不复制猜测

设备树是硬件描述与驱动绑定之间的合同,不是把寄存器地址堆在一个文本文件里。compatible、reg、interrupts、clocks、resets、pinctrl、power supplies、DMA 和 phandle 共同描述资源;驱动 probe、defer、remove 与用户空间接口则把合同变成行为。

看懂用 binding 约束描述硬件,而不是迎合当前驱动实现。
说清定位 probe、依赖、pinctrl 和中断问题。
开始做为 sysfs、设备节点和用户 API 保存可追踪证据。
官方基线Linux kernel documentation内核启动、设备树、驱动与用户空间接口按课程内核系列查证。锁定:6.18.7 (课程 defconfig) · 核对日期:2026-09-05

13.2.1 DTS、DTSI 与 DTB 的边界

DTS 源文件经过预处理和编译形成 DTB,bootloader 将它与内核一起交接。节点应描述板上真实存在的硬件和资源,`compatible` 让驱动按能力匹配;节点名不是稳定 ABI。Linux 设备树 binding 文档强调属性约束、示例和 ABI 兼容,新增属性要说明单位、枚举和依赖。

13.2.2 compatible 与 binding schema

一个驱动通常同时消费多个 provider:时钟、复位、regulator、pinctrl、DMA 和中断。phandle 只表达引用,不能保证 provider 已经初始化;内核可能返回 `-EPROBE_DEFER`,稍后重试。若日志只显示‘设备不存在’,应检查依赖链和 probe 顺序,而不是把节点复制到根目录。

PYTHON · DT RESOURCE CHECK
REQUIRED = {'compatible', 'reg'} def validate_node(node): missing = REQUIRED - set(node) if missing: raise ValueError(f'missing: {sorted(missing)}') return node['compatible'], tuple(node['reg'])

主机验证节点字段和资源形状;pinctrl、电平、中断和 probe 只能由目标机补证。

C · DRIVER PROBE SHAPE
static int demo_probe(struct platform_device *pdev) { struct resource *mem; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return -ENODEV; dev_info(&pdev->dev, "resource %pa\n", &mem->start); return 0; }

驱动骨架不代表硬件可用;要继续核对 binding、时钟、复位、IRQ、DMA 和电气波形。

13.2.3 资源 phandle、时钟与复位

pinctrl 和 regulator 把电气条件带入软件。active、idle、sleep 三组引脚状态可能切换方向、上下拉和驱动能力;电源 regulator 可能需要毫秒级稳定时间或先后顺序。主机解析可以发现缺少属性,但只有示波器、万用表和目标板才能证明电平、上电时序和外设真的响应。

表 2 · 设备树与驱动:描述硬件,不复制猜测的证据判断表
对象应留下的证据不能直接推出
描述binding/compatible节点名是稳定 ABI
资源reg/irq/clock/pinctrlprobe 成功代表外设工作
接口/sys /dev/debugfs任何文件都可长期依赖

13.2.4 pinctrl、regulator 与上电顺序

中断处理需要确认来源、清除顺序、共享线、线程化处理和锁上下文;DMA 还要考虑缓存一致性、对齐、IOMMU/地址宽度和生命周期。设备树写了 IRQ 号并不代表硬件会产生该电平,驱动 probe 成功也不代表一次传输没有丢中断。测试要保存 irq count、错误计数和一条原始波形。

13.2.5 probe、deferred probe 与 remove

驱动对用户空间暴露的接口有不同稳定性:sysfs 适合属性,`/dev` 设备节点承载 read/write/ioctl,debugfs 只用于调试而不应被产品依赖,netlink 或字符设备适合事件流。权限、udev 规则、阻塞/非阻塞和错误码都是合同;写入一个文件成功只证明 VFS 接受,不保证硬件完成动作。

图 2.1 · 设备树与驱动:描述硬件,不复制猜测的判断路径每一格都要有自己的条件。
描述DTS/binding
资源reg/irq/clock
绑定compatible/probe
接口/sys /dev

从左到右是思考顺序,不是把启动、设备树、用户空间和交付证据压缩成一个“通过”。

13.2.6 中断、DMA 与并发边界

同一设备可能由内核内置驱动、模块、固件加载和用户空间守护进程共同完成。模块版本、签名、固件文件、udev、设备节点和服务依赖要一起发布。设备树 overlay 可以动态增加节点,但它也会改变资源占用和生命周期;生产产品应明确允许的 overlay 与回滚方式。

13.2.7 sysfs、/dev 与用户空间 ABI

设备树驱动卡包括 board、compatible、binding_version、status、reg、interrupts、clocks、resets、pinctrl、supplies、dma、firmware、probe_order、defer_reason、userspace_abi、permissions、host_parse、target_measurement 和 unsupported_scope。它把‘驱动没反应’拆成可验证事实。

13.2.8 本节交付:设备树驱动卡

一个安全的单变量练习是只改一个属性,例如把 `status = "disabled"` 改成 `okay`,再比较 DTB、probe、/sys 和波形;不要同时换内核、rootfs、pinctrl 和供电。如果现象消失,要保留旧 DTB、命令行和日志,以便证明是哪一个合同条件改变了。

CHECKPOINT 13.2把“设备树怎样让同一个内核知道不同板子的真实硬件?”写成一份可复查记录

请围绕“设备树与驱动:描述硬件,不复制猜测”写出板卡与版本、输入和前置条件、启动/驱动/服务/存储机制、输出与完成语义、一个安全或资源约束、一个失败路径和对应证据。没有目标板时,运行主机路径并把未执行字段标为未执行;拥有目标机时,还要补齐 SoC、bootloader、内核、DTB、rootfs、工具链、供电、存储、温度、负载和观察条件。不要用一次启动、一个 probe、一次 QEMU、一个哈希或一行日志覆盖没有做过的验证。

合格记录要让没有现场上下文的读者知道先查什么、先运行什么、看到什么才算通过,以及哪一句结论仍然不能推出。未知项、停止条件、恢复镜像、权限边界、许可证和回滚动作都是交付的一部分。

03 · KERNEL AND ROOTFS

内核与根文件系统:把构建输入固定下来

嵌入式 Linux 的构建不是一次 `make`,而是一组交叉工具链、内核 Kconfig、设备树、bootloader、包选择、rootfs 生成、镜像布局和版本锁定的组合。Buildroot 适合以单一配置生成系统;Yocto/OpenEmbedded 通过 BitBake、recipes、layers 和 machine/distro policy 组织可复用发行版。

看懂区分交叉编译器、sysroot、内核配置和 rootfs 包集合。
说清比较 Buildroot 与 Yocto 的输入、输出和维护边界。
开始做让构建可复现、可审计、可缩小和可回滚。
官方基线Buildroot User Manual下载、配置、构建、复现和法律信息按 2026.08 手册验收。锁定:2026.08 · 核对日期:2026-09-05

13.3.1 架构、工具链与 sysroot

交叉构建至少有四类输入:源码与补丁、工具链与 sysroot、配置与策略、目标机描述。编译器能生成目标架构代码,不代表目标机的动态链接器、glibc/musl、内核 ABI 和硬件指令都匹配;`readelf -h`、`file`、`ldd`(在目标机)和 sysroot 清单要一起看。

13.3.2 Kconfig、defconfig 与最小内核

内核 Kconfig 决定驱动、文件系统、调试和安全选项,defconfig 是起点而不是完整解释。模块可能在 rootfs 外部,固件可能由驱动请求,initramfs 可能包含另一份工具。构建记录要保存最终 `.config`、生成的 DTB、模块列表、固件文件和命令行,避免‘菜单里勾过’成为不可复查证据。

PYTHON · BUILD MANIFEST
from dataclasses import dataclass @dataclass(frozen=True) class Artifact: name: str source_commit: str sha256: str target: str def manifest_ok(items): return bool(items) and all(item.source_commit and item.sha256 and item.target for item in items)

主机验证产物清单完整;交叉编译器、内核 ABI、许可证和目标机启动仍需补证。

C · CROSS COMPILE SHAPE
${CROSS_COMPILE}gcc --sysroot=$SYSROOT -O2 -ffile-prefix-map=$PWD=/src -c app.c -o app.o ${CROSS_COMPILE}readelf -h app.o

命令展示输入关系;CROSS_COMPILE、sysroot、CPU flags 和工具链版本必须固定。

13.3.3 内核模块、固件与 rootfs 文件

Buildroot 将工具链、内核、bootloader、rootfs 和镜像放入一条相对直接的配置路径,适合单产品或受控系统;它的产物与配置仍需锁定版本和外部源码。Yocto/OpenEmbedded 以 BitBake 为任务执行器,用 layers、recipes、classes、machine 和 distro 分开元数据,适合多个产品线但学习和维护成本更高。

表 3 · 内核与根文件系统:把构建输入固定下来的证据判断表
对象应留下的证据不能直接推出
输入source/toolchain/config缓存命中就是复现
系统kernel/rootfs/SDK镜像越小越安全
证据manifest/hash/license一次构建覆盖所有版本

13.3.4 Buildroot 的单配置路径

BSP 层应承载机器专属配置,软件层承载通用 recipe,distro 层承载政策;把整个 recipe 从别的层复制进项目会失去上游更新和可审计性。每次构建要记录 layer revision、优先级、bbappend、PACKAGECONFIG、下载源和许可证,尤其要检查新增层是否破坏原有的可复现承诺。

13.3.5 Yocto 的 layers、recipes 与 BitBake

可复现不是‘今天构建两次一样’这么简单。输入源码、时间戳、主机路径、locale、编译器、下载镜像、缓存和随机数都可能影响产物;Yocto 文档把路径映射、recipe sysroot、HOSTTOOLS 和 hash equivalence 纳入复现设计。发布前应比较独立目录、干净缓存和固定时间策略的哈希,并保存差异。

图 3.1 · 内核与根文件系统:把构建输入固定下来的判断路径每一格都要有自己的条件。
输入source/config
编译toolchain/sysroot
组装kernel/rootfs
复现hash/manifest

从左到右是思考顺序,不是把启动、设备树、用户空间和交付证据压缩成一个“通过”。

13.3.6 BSP、machine 与 distro 配置

最小 rootfs 只表示包少,不表示攻击面小或可运维。删掉 shell、证书、时区、固件、日志工具和恢复程序可能让现场无法诊断;留下编译器、默认密码、调试服务和过宽权限又会放大风险。包清单要按运行时依赖、调试依赖、恢复依赖和合规许可证分层。

13.3.7 可复现构建、缓存与许可证

构建输入卡包括 source_commits、patches、toolchain、sysroot、arch、defconfig、dtb、bootloader_config、rootfs_packages、BSP/layers、recipe_revisions、download_mirror、license_manifest、reproducibility_run、artifact_hash、host_result、target_result 和 known_gaps。它让读者知道‘重新构建’的具体含义。

13.3.8 本节交付:构建输入卡

最小可复现实验可以只编译一个主机工具:固定源文件、编译器路径、CFLAGS、环境变量和输出目录,比较两次哈希,再故意改变绝对路径或时间戳观察差异。这个结果不能证明完整 Linux 镜像已经复现,但能让读者理解输入固定和证据保存为何属于产品工程。

CHECKPOINT 13.3把“怎样从源码、配置和层生成一份可解释的 Linux 系统?”写成一份可复查记录

请围绕“内核与根文件系统:把构建输入固定下来”写出板卡与版本、输入和前置条件、启动/驱动/服务/存储机制、输出与完成语义、一个安全或资源约束、一个失败路径和对应证据。没有目标板时,运行主机路径并把未执行字段标为未执行;拥有目标机时,还要补齐 SoC、bootloader、内核、DTB、rootfs、工具链、供电、存储、温度、负载和观察条件。不要用一次启动、一个 probe、一次 QEMU、一个哈希或一行日志覆盖没有做过的验证。

合格记录要让没有现场上下文的读者知道先查什么、先运行什么、看到什么才算通过,以及哪一句结论仍然不能推出。未知项、停止条件、恢复镜像、权限边界、许可证和回滚动作都是交付的一部分。

04 · USER SPACE

用户空间:进程、服务与 IPC

进入用户空间后,问题从寄存器转为进程、文件描述符、权限、服务依赖、IPC、日志、看门狗和升级兼容。BusyBox init、systemd 或自定义 supervisor 都能成为 PID 1,但它们对依赖、重启、隔离、日志和关机的语义不同。

看懂用进程、服务、依赖和权限描述运行系统。
说清选择合适的 IPC 与超时,而不是把所有功能塞进一个守护进程。
开始做为启动、重启、降级和退出留下证据。
官方基线Buildroot User Manual下载、配置、构建、复现和法律信息按 2026.08 手册验收。锁定:2026.08 · 核对日期:2026-09-05

13.4.1 PID 1 与 init 选择

PID 1 的特殊性是它不能像普通进程一样随意退出;它要回收孤儿、响应信号并负责继续启动。systemd 用 unit、target、依赖和状态管理服务,BusyBox init 更小但需要脚本自己处理顺序和重启。选择时要把镜像大小、启动时间、隔离能力、日志、维护团队和恢复路径写成约束。

13.4.2 服务 unit 与依赖图

服务依赖应表达‘需要什么’和‘准备好是什么’,而不是盲目 `sleep 5`。网络设备可能有 carrier、IP、DNS、TLS 和业务 readiness 多层状态;一个服务启动成功不等于它的依赖可用。systemd 的 `After=` 只规定排序,`Requires=`/`Wants=` 和健康检查承担不同语义,不能混写成一个绿色灯。

PYTHON · SERVICE CONTRACT
from dataclasses import dataclass @dataclass(frozen=True) class ServiceResult: name: str ready: bool exit_code: int reason: str def restartable(result): return not result.ready and result.exit_code != 0 and bool(result.reason)

主机模拟 readiness/退出证据;PID 1、cgroup、权限和真实时序仍需目标系统验证。

C · UNIX SOCKET SHAPE
int fd = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, "/run/demo.sock", sizeof(addr.sun_path) - 1); /* bind/listen/accept and length-prefixed frames omitted */

接口展示 IPC 形状;路径权限、服务依赖、超时和协议版本需要在目标 rootfs 上验收。

13.4.3 readiness、liveness 与 watchdog

本地 IPC 也需要版本、长度、编码、超时、错误和关闭语义。Unix domain socket 适合请求/响应,pipe 适合简单流,共享内存适合高吞吐但需要同步;把 Python 字典或 C struct 直接跨进程写入通常会引入端序、对齐和升级问题。主机模型应注入截断、重复和对端重启。

表 4 · 用户空间:进程、服务与 IPC的证据判断表
对象应留下的证据不能直接推出
启动PID1/unit/dependencysleep 能表达依赖
通信IPC/schema/timeout本地 socket 不需要合同
治理UID/cap/log/watchdogroot 运行最省事

13.4.4 Unix socket、pipe 与消息合同

最小权限不仅是 UID。文件目录、设备节点、CAP_NET_RAW、CAP_SYS_ADMIN、摄像头/音频、网络绑定端口和 cgroup 资源都属于权限面。服务应使用专用用户、只读根、可写状态目录和明确的 capability;`sudo` 或 root 运行一次成功不能证明权限设计可交付。

13.4.5 权限、UID、capability 与目录

升级与关机需要可观测的退出协议:先停止接收新请求,刷写必要状态,关闭 socket,停止硬件输出,再在 deadline 内退出。systemd 的 `TimeoutStopSec`、Restart 策略和 watchdog 只提供框架,应用仍要处理半完成事务、锁、子进程和电源丢失。退出码、信号和最后日志要进入证据记录。

图 4.1 · 用户空间:进程、服务与 IPC的判断路径每一格都要有自己的条件。
启动PID 1
依赖unit/ready
通信IPC/socket
治理权限/日志

从左到右是思考顺序,不是把启动、设备树、用户空间和交付证据压缩成一个“通过”。

13.4.6 信号、退出码与优雅关机

日志系统要同时考虑可见性、隐私、存储和恢复。journald、syslog、环形文件、远程转发和独立数据分区各有代价;日志轮转需要按大小、时间和错误级别限制,启动早期没有持久存储时要有内存缓冲。日志记录密码、token 或完整报文会把诊断变成泄露源。

13.4.7 日志、轮转与持久化策略

用户空间服务卡包括 init_choice、unit_graph、dependencies、readiness、liveness、ipc_schema、timeouts、uid/gid、capabilities、paths、signals、exit_codes、watchdog、log_policy、storage_quota、host_result、target_result 和 recovery_action。它把‘服务能跑’拆成可回归合同。

13.4.8 本节交付:用户空间服务卡

一个可复现主机实验可以起两个 Python 子进程,用 Unix socket 发送带长度前缀的 JSON,故意截断一帧并观察超时与退出码。它不能证明 systemd unit、内核 cgroup 或真实硬件服务已通过,但能验证协议边界、幂等重连和停止窗口是否写清楚。

CHECKPOINT 13.4把“Linux 启动后,服务怎样在资源和故障中保持可观察?”写成一份可复查记录

请围绕“用户空间:进程、服务与 IPC”写出板卡与版本、输入和前置条件、启动/驱动/服务/存储机制、输出与完成语义、一个安全或资源约束、一个失败路径和对应证据。没有目标板时,运行主机路径并把未执行字段标为未执行;拥有目标机时,还要补齐 SoC、bootloader、内核、DTB、rootfs、工具链、供电、存储、温度、负载和观察条件。不要用一次启动、一个 probe、一次 QEMU、一个哈希或一行日志覆盖没有做过的验证。

合格记录要让没有现场上下文的读者知道先查什么、先运行什么、看到什么才算通过,以及哪一句结论仍然不能推出。未知项、停止条件、恢复镜像、权限边界、许可证和回滚动作都是交付的一部分。

05 · STORAGE AND SECURITY

存储与安全:让数据、密钥和更新可恢复

嵌入式 Linux 的 Flash 既存放内核和 rootfs,也存放配置、日志、密钥、计数器和升级状态。只读根、overlayfs、UBI/UBIFS、分区表、A/B 镜像、U‑Boot 环境、dm-verity、签名和加密必须按数据生命周期组合,而不是复制一个‘安全启动’开关。

看懂区分代码、配置、日志、密钥和计数器的写入策略。
说清设计掉电安全、版本兼容和回滚路径。
开始做把密钥、调试口和恢复介质纳入发布门禁。
官方基线Linux kernel documentation内核启动、设备树、驱动与用户空间接口按课程内核系列查证。锁定:6.18.7 (课程 defconfig) · 核对日期:2026-09-05

13.5.1 代码、状态与日志分区

系统镜像和运行状态的写入特征不同。squashfs 或 dm-verity 适合不可变系统,overlayfs 把可写 upper 层与只读 lower 层组合;配置、队列、日志和密钥需要独立生命周期。把所有东西放在一个 ext4 分区虽然开始简单,但升级、磨损、取证和回滚会互相干扰。

13.5.2 只读根与 overlayfs

原子配置更新常见路径是写临时文件、校验、`fsync` 文件、原子 rename,再 `fsync` 目录;还要保留旧版本或备份。数据库、日志和密钥有额外的事务和权限约束。即便 POSIX 调用返回成功,断电、缓存、控制器和介质仍可能使目标机结果不同,必须在目标机复核。

PYTHON · AB UPDATE
def update_verdict(active, candidate_hash, expected_hash, health_ok): if not candidate_hash or candidate_hash.lower() != expected_hash.lower(): return 'REJECT' if active == 'slot-a' and health_ok: return 'PROMOTE_B' if active == 'slot-b' and health_ok: return 'PROMOTE_A' return 'ROLLBACK'

主机验证槽位、摘要和健康门禁;U-Boot、Flash、掉电和签名链仍需目标机演练。

C · ATOMIC CONFIG SHAPE
int fd = open("/data/config.tmp", O_WRONLY|O_CREAT|O_TRUNC, 0600); write(fd, bytes, length); fsync(fd); close(fd); rename("/data/config.tmp", "/data/config");

tmp+fsync+rename 是设计起点;介质缓存、掉电、权限和备份策略必须按实际文件系统测试。

13.5.3 UBI/UBIFS 与 eMMC/ext4

A/B 更新通常先写未启动槽,校验签名/哈希和兼容性,再设置 pending,第一次启动完成健康检查后标记 valid;失败或超时回滚。启动计数和槽位状态要由可靠组件共同维护,升级脚本应可重复执行、可中断、可恢复,且不把旧槽覆盖成不可启动状态。

表 5 · 存储与安全:让数据、密钥和更新可恢复的证据判断表
对象应留下的证据不能直接推出
布局boot/root/state所有数据放同一分区
更新A/B/sign/health下载完成即可切槽
恢复env/recovery/powercut回滚只靠人工

13.5.4 原子写入、fsync 与掉电

签名回答‘谁批准这份镜像’,哈希回答‘内容是否与摘要一致’,verity 保护运行时块完整性,加密保护内容机密性,TPM/eFuse/安全元件提供密钥根。它们相互补充而不是互换;关闭签名验证来修复启动问题,只会把调试便利变成生产漏洞。

13.5.5 A/B 更新与启动计数

U‑Boot 环境保存启动槽、计数和参数时要考虑冗余、CRC、磨损、并发写和恢复默认值。救援介质可以是串口下载、USB、SD、网络或专用 recovery 分区,但每个入口都要有身份、授权和安全输出。量产设备不能依赖工程师临时记住一条命令。

图 5.1 · 存储与安全:让数据、密钥和更新可恢复的判断路径每一格都要有自己的条件。
布局boot/root/state
写入atomic/fsync
信任sign/verity/key
恢复A/B/recovery

从左到右是思考顺序,不是把启动、设备树、用户空间和交付证据压缩成一个“通过”。

13.5.6 签名、verity、加密与密钥

存储安全卡包括 media、partitions、mounts、ro_root、overlay、write_policy、fsync、power_cut_test、slot_state、boot_counter、signature、verity、encryption、key_owner、env_redundancy、recovery_media、host_result、target_result 和 stop_conditions。它把‘可升级’变成一条证据链。

13.5.7 U-Boot 环境与救援介质

主机可先模拟两个槽位和一个 manifest:下载只写 inactive slot,摘要错误返回 reject,首次健康检查失败返回 rollback。这个模型能验证状态机和不可越界结论,却不能证明 U‑Boot、Flash 控制器或断电时序;目标机实验必须在有恢复镜像的测试设备上进行。

13.5.8 本节交付:存储安全卡

交付问题: 提交布局、写入、密钥和回滚证据。交付动作是“把卡交给调试交付单元”,相应输入、输出或未执行边界必须在记录中逐项对应。

CHECKPOINT 13.5把“根文件系统、数据分区和安全启动怎样共同面对断电与升级?”写成一份可复查记录

请围绕“存储与安全:让数据、密钥和更新可恢复”写出板卡与版本、输入和前置条件、启动/驱动/服务/存储机制、输出与完成语义、一个安全或资源约束、一个失败路径和对应证据。没有目标板时,运行主机路径并把未执行字段标为未执行;拥有目标机时,还要补齐 SoC、bootloader、内核、DTB、rootfs、工具链、供电、存储、温度、负载和观察条件。不要用一次启动、一个 probe、一次 QEMU、一个哈希或一行日志覆盖没有做过的验证。

合格记录要让没有现场上下文的读者知道先查什么、先运行什么、看到什么才算通过,以及哪一句结论仍然不能推出。未知项、停止条件、恢复镜像、权限边界、许可证和回滚动作都是交付的一部分。

06 · DEBUG AND DELIVERY

调试与交付:从串口证据到长期维护

嵌入式 Linux 的最后难题是可维护性:串口、earlycon、动态调试、ftrace、perf、eBPF(若启用)、core dump、pstore、远程日志、CI、SBOM、签名、工厂烧录和现场回滚要形成同一份交付记录。开发、验收和生产的工具权限必须分层。

看懂建立从启动、驱动、服务到性能的证据索引。
说清把主机可复现步骤和目标机边界写给读者。
开始做提交版本、测试、发布和停止条件完整的交付包。
官方基线Buildroot User Manual下载、配置、构建、复现和法律信息按 2026.08 手册验收。锁定:2026.08 · 核对日期:2026-09-05

13.6.1 早期串口、earlycon 与启动日志

earlycon、串口和 pstore 构成启动/崩溃的早期证据链,但串口波特率、引脚复用、时钟和电平必须与板子一致。日志要记录内核、DTB、bootloader、rootfs 和服务版本;如果只保存文本而没有对应镜像哈希,未来无法知道日志来自哪次发布。

13.6.2 dynamic_debug、ftrace 与 trace-cmd

ftrace、dynamic_debug、perf、strace 和 gdbserver 的侵入性不同。跟踪器会占 CPU、内存、锁和 I/O,断点会改变时序,远程 gdb 还会改变调度。性能记录要包含工具、过滤器、采样率、内核配置和探针开销;‘开启 trace 后问题消失’本身是证据,不是修复。

PYTHON · RELEASE GATE
def release_gate(host_ok, target_ok, recovery_image, known_limits): if not host_ok or not target_ok: return 'BLOCKED' if not recovery_image: return 'BLOCKED_NO_RECOVERY' return 'READY' if known_limits else 'BLOCKED_NO_LIMITS'

主机门禁提醒证据和恢复边界;它不会伪造 QEMU、HIL、供电或板卡性能结果。

C · EARLY LOG SHAPE
pr_info("chapter13: stage=%s build=%s\n", stage_name, build_id); trace_printk("demo_event=%u\n", event_id);

观测接口会扰动时序并受内核配置限制;发布前要保存符号、配置和关闭调试口的证据。

13.6.3 perf、procfs 与资源基线

资源基线应覆盖启动时间、空闲/业务 CPU、RSS/峰值内存、I/O、温度、网络吞吐和文件系统写入。`/proc`、`/sys`、cgroup 和 perf 的字段受内核配置与权限影响,不能把一个平台的数字直接比较到另一块板。每项指标都要绑定工作负载、窗口和采样条件。

表 6 · 调试与交付:从串口证据到长期维护的证据判断表
对象应留下的证据不能直接推出
观察serial/trace/metric探针不会改变行为
构建CI/QEMU/HILQEMU 就是目标板
发布SBOM/sign/recovery开发镜像可直接量产

13.6.4 崩溃、pstore 与 core dump

崩溃取证需要保存 oops/panic、reset cause、pstore、core dump、符号、System.map、内核配置、设备树和最后的服务日志。压缩和远程上传也会占用存储与网络;在满盘、网络不可用或重复崩溃时应进入受控降级。恢复动作先保护执行器和文件系统,再做诊断。

13.6.5 CI 中的交叉构建与 QEMU

QEMU、容器和主机单元测试适合验证命令行、配置解析、IPC、A/B 状态和日志处理;它们不能替代真实 pinctrl、DMA、EMMC 掉电、热、电源噪声和板卡时序。CI 应把 host smoke、交叉构建、镜像签名、QEMU boot 和 target HIL 分成不同门禁,避免绿色构建掩盖缺失的硬件证据。

图 6.1 · 调试与交付:从串口证据到长期维护的判断路径每一格都要有自己的条件。
看见UART/log
解释trace/metric
复现host/target
交付CI/recovery

从左到右是思考顺序,不是把启动、设备树、用户空间和交付证据压缩成一个“通过”。

13.6.6 板卡实验室与 HIL 边界

发布包要携带版本 manifest、源码 commit、工具链、DTB、内核、rootfs、bootloader、SBOM、签名摘要、分区/烧录脚本、恢复镜像、测试报告和已知限制。签名私钥不进入仓库,工厂烧录记录要能追到设备身份和批次。回滚不是口头承诺,应在同修订测试板上演练。

13.6.7 SBOM、签名、发布和回滚

Linux 交付记录包括 board、boot_chain、kernel、dtb、rootfs、services、storage、security、observability、performance、host_tests、qemu_result、hil_result、release_artifacts、sbom、signature、recovery、known_limits 和 stop_conditions。它是读者复现、工程师定位和现场维护共享的索引。

13.6.8 本节交付:Linux 交付记录

第 13 章的主机路径应先让读者运行纯 Python 模型、解析示例配置、检查镜像/分区 manifest,再决定是否进入 QEMU 或目标板。没有工具链或板子时,交付记录要明确写‘未执行’,而不是用构建成功替代硬件验证;有目标机时,补上芯片、内核、DTB、供电、温度和测量条件。

CHECKPOINT 13.6把“怎样把一套 Linux 板卡系统交给读者、测试和现场,而不是只交一张镜像?”写成一份可复查记录

请围绕“调试与交付:从串口证据到长期维护”写出板卡与版本、输入和前置条件、启动/驱动/服务/存储机制、输出与完成语义、一个安全或资源约束、一个失败路径和对应证据。没有目标板时,运行主机路径并把未执行字段标为未执行;拥有目标机时,还要补齐 SoC、bootloader、内核、DTB、rootfs、工具链、供电、存储、温度、负载和观察条件。不要用一次启动、一个 probe、一次 QEMU、一个哈希或一行日志覆盖没有做过的验证。

合格记录要让没有现场上下文的读者知道先查什么、先运行什么、看到什么才算通过,以及哪一句结论仍然不能推出。未知项、停止条件、恢复镜像、权限边界、许可证和回滚动作都是交付的一部分。

资料卡(访问日期:2026-09-05):本章对照 Linux Kernel、U-Boot、Yocto、Buildroot 与 systemd 官方文档。主机模型不替代真实 bootloader、DDR、DTB、驱动、电源、Flash、签名、QEMU/HIL 和目标板证据。