3、嵌入式Linux系统移植:U-Boot编译与烧录、Linux内核裁剪与设备树配置、根文件系统构建

系统移植,说白了就是给硬件平台「穿上」Linux 的衣服。很多做 AI 应用的工程师觉得这步太底层,直接买开发板用现成的镜像就好。但做边缘 AI 产品不一样——你要裁剪内核、定制驱动、优化启动时间,甚至要适配非标准的传感器接口。我自己带项目时吃过这个亏:第一版原型直接用厂商 BSP,结果量产时发现 Flash 空间不够、启动慢了 2 秒被客户投诉。从那以后,我坚持自己走一遍移植流程。

这一章,我们聚焦三个核心环节:U-Boot(引导加载程序)、Linux 内核(含设备树)、根文件系统。我会结合一个典型的 ARM Cortex-A72 平台(比如 RK3588 或 i.MX8M Plus)来讲解。

本章知识体系:U-Boot 编译与烧录 → 内核裁剪与设备树配置 → 根文件系统构建(Buildroot / Yocto)
嵌入式 Linux 系统移植核心流程 硬件平台 U-Boot 编译与烧录 Linux 内核裁剪 + 设备树 根文件系统构建 可启动系统镜像 三个环节环环相扣,任何一个出问题系统都起不来

3.1 U-Boot 编译与烧录

U-Boot 是硬件上电后第一个跑的程序。它的任务很简单:初始化 DDR、时钟、存储控制器,然后加载内核到内存并跳转。但简单不代表容易——我见过太多人卡在 U-Boot 阶段,串口只打印一个「CC」就死掉了。

3.1.1 获取与配置

从 官方仓库 克隆代码,或者直接用芯片厂商提供的版本。我个人习惯用厂商版本,因为里面已经包含了板级配置和 DDR 初始化代码。

# 以 RK3588 为例
git clone https://github.com/rockchip-linux/u-boot.git
cd u-boot
make rk3588_defconfig
make menuconfig

在 menuconfig 里,你需要关注几个关键项:

  • Boot media:选择从 eMMC、SD 卡还是 SPI NOR Flash 启动。
  • Network support:如果要用 TFTP 下载内核,必须开启。
  • Environment storage:环境变量保存在哪里?我一般放 eMMC 的特定分区。
小技巧:第一次移植时,先别急着裁剪。保留所有调试选项(DEBUG、early printf),等系统跑起来再关掉。我曾经为了省 2KB 空间关掉调试输出,结果花了三天找 bug。

3.1.2 编译与烧录

编译命令很简单,但要注意交叉编译工具链的路径:

export CROSS_COMPILE=aarch64-linux-gnu-
make -j$(nproc)

编译产物通常是 u-boot.bin 或 idbloader.img(取决于平台)。烧录方式分两种:

烧录方式 适用场景 命令示例
SD 卡 开发调试阶段 dd if=u-boot.bin of=/dev/sdb seek=64
eMMC 量产烧录 使用厂商工具(如 rkdeveloptool)
SPI Flash 小容量启动 flashcp u-boot.bin /dev/mtd0
注意:烧录前务必确认偏移地址。RK3588 的 idbloader 要烧到偏移 64 扇区(32KB),烧错位置板子直接变砖。我刚开始时烧错过三次,后来养成了「烧前 double-check」的习惯。

3.2 Linux 内核裁剪与设备树配置

内核是系统的核心,但默认配置往往包含大量你不需要的驱动。对于边缘 AI 设备,我们要做减法——去掉蓝牙、Wi-Fi(如果不用)、去掉多余的文件系统支持、去掉调试符号。

3.2.1 内核裁剪

从厂商提供的 defconfig 开始,逐步裁剪:

make ARCH=arm64 rockchip_linux_defconfig
make menuconfig

我通常会重点检查这几项:

  • General setup → Kernel compression mode:选 LZ4 或 LZO,解压更快。
  • Device Drivers:只保留你硬件上有的外设。比如你的摄像头是 MIPI CSI,就别选 USB Camera。
  • File systems:如果只用 SquashFS 或 ext4,去掉其他。
  • Networking support:边缘 AI 设备通常不需要完整的网络栈,可以裁剪掉 IPv6、802.11 等。

裁剪后,内核镜像大小可以从 15MB 降到 5MB 左右。启动时间也能缩短 1-2 秒。

经验之谈:裁剪时别太激进。我有个项目把 SCSI 支持去掉了,结果 eMMC 驱动依赖它,系统直接 panic。建议每改一次配置就编译一次,确保能启动。

3.2.2 设备树配置

设备树(Device Tree)描述了硬件拓扑结构。没有它,内核不知道你的 GPIO 接了什么、I2C 地址是多少、DDR 大小是多少。

设备树源文件(.dts)通常放在 arch/arm64/boot/dts/rockchip/ 下。以 RK3588 为例:

/dts-v1/;
#include "rk3588.dtsi"

/ {
    model = "Edge AI Board v1.0";
    compatible = "rockchip,rk3588";

    chosen {
        stdout-path = &uart2;
    };

    memory@a0000000 {
        device_type = "memory";
        reg = <0x0 0xa0000000 0x0 0x80000000>;  // 2GB
    };
};

&i2c2 {
    status = "okay";
    imx219: camera@10 {
        compatible = "sony,imx219";
        reg = <0x10>;
        clocks = <&cru CLK_CAMERA>;
        // ... 其他属性
    };
};

编译设备树:

make ARCH=arm64 dtbs

生成 rk3588-edge-ai-board.dtb,和内核镜像一起打包。

避坑指南:我曾经在设备树里把 GPIO 的 pinmux 写错了,结果摄像头始终无法初始化。排查了三天,最后用 cat /sys/kernel/debug/gpio 才发现引脚复用冲突。建议每次改完 dts 后,先检查 pinmux 是否正确。

3.3 根文件系统构建

根文件系统是 Linux 的用户空间基础。它包含 init 程序、库文件、配置文件、工具链等。对于嵌入式设备,我们通常用 Buildroot 或 Yocto 来构建。

3.3.1 Buildroot:轻量级选择

Buildroot 适合对大小敏感的项目。它用 Kconfig 配置,和内核风格一致,上手快。

git clone https://git.buildroot.net/buildroot
cd buildroot
make qemu_aarch64_virt_defconfig  # 或自己从头配
make menuconfig

关键配置项:

  • Target options:选择架构(AArch64)和 ABI。
  • Toolchain:可以用外部工具链,也可以让 Buildroot 自己编译。
  • System configuration:设置主机名、root 密码、启动脚本。
  • Target packages:只选你需要的。比如 AI 推理需要 OpenCV、TensorFlow Lite,但不需要 X11 或 Qt。

编译完成后,在 output/images/ 下得到 rootfs.ext4、rootfs.cpio 等镜像。

个人建议:对于边缘 AI 设备,文件系统类型选 SquashFS(只读压缩)或 ext4(可读写)。SquashFS 更安全,防止误修改;ext4 方便调试。我一般调试阶段用 ext4,量产切 SquashFS。

3.3.2 Yocto:企业级方案

Yocto 更复杂,但定制性更强。它适合需要大量自定义软件包、多平台支持的项目。

基本流程:

git clone git://git.yoctoproject.org/poky
cd poky
source oe-init-build-env
bitbake core-image-minimal

Yocto 的 layer 机制很强大。你可以为 AI 推理引擎单独写一个 layer:

meta-edge-ai/
├── conf/
│   └── layer.conf
├── recipes-support/
│   └── tflite/
│       ├── tflite_2.14.0.bb
│       └── files/
│           └── tflite.rules

然后在 local.conf 中添加:

IMAGE_INSTALL_append = " tflite opencv python3"
特性 Buildroot Yocto
学习曲线 低(1-2 天) 高(1-2 周)
镜像大小 小(可到 4MB) 较大(通常 50MB+)
包管理 无(静态编译) 有(RPM/DEB)
适用场景 资源受限设备 复杂产品、多平台
注意:Yocto 第一次编译会下载大量源码,网络不好时可能失败。建议先配置本地 sstate 缓存,或者用国内镜像源。我第一次跑 Yocto 时,光下载就花了 6 个小时。

3.4 整合与启动验证

三个组件都准备好后,把它们打包成可启动镜像。以 SD 卡为例:

# 分区:第一个分区放内核和设备树,第二个分区放根文件系统
sudo fdisk /dev/sdb
# 创建两个分区:64MB 的 FAT32 + 剩余空间的 ext4

# 拷贝内核和设备树
sudo mount /dev/sdb1 /mnt/boot
sudo cp Image /mnt/boot/
sudo cp rk3588-edge-ai-board.dtb /mnt/boot/
sudo umount /mnt/boot

# 拷贝根文件系统
sudo mount /dev/sdb2 /mnt/rootfs
sudo tar -xvf rootfs.tar -C /mnt/rootfs/
sudo umount /mnt/rootfs

插卡上电,串口输出应该能看到 U-Boot 启动、加载内核、挂载根文件系统,最后出现登录提示符。

验证清单:
  • U-Boot 能识别 DDR 容量和存储设备
  • 内核能挂载根文件系统(无 panic)
  • 设备树中的外设(I2C、SPI、GPIO)能正常工作
  • AI 推理库(如 TensorFlow Lite)能加载模型

嗯,到这里系统移植的核心流程就走通了。你可能会遇到各种奇怪的问题——DDR 时序不对、设备树中断号写错、文件系统缺少动态链接库。别慌,每个问题都是学习的机会。我当年第一次移植成功时,看着串口打印出 Welcome to Buildroot,那种成就感比写十年代码都强。