第4章:输出产物解析
Buildroot 编译完了,output 目录里到底装了啥?
说实话,我第一次用 Buildroot 的时候,编译完直接懵了。output 目录下文件一大堆,哪些是我们要的?哪些是中间产物?今天我就带你把这个目录扒个底朝天。
4.1 output 目录结构总览
先看整体结构。Buildroot 默认的 output 目录长这样:
output/
├── build/ # 每个软件包的编译目录
├── host/ # 主机工具链和交叉编译工具
├── images/ # 最终生成的镜像文件(重点!)
├── staging/ # 目标系统的头文件和库
├── target/ # 目标文件系统的根目录
└── graphs/ # 编译依赖图(可选)
嗯,这里要注意:images 目录才是我们的宝贝。其他目录说白了都是中间产物,除非你要调试或者二次开发,否则平时不用管。
核心结论:你最终要烧录到板子上的东西,全在 output/images/ 里。
4.2 images 目录详解
我们拿一个典型的 ARM 板子(比如树莓派 3)来举例。编译完成后,images 目录大概长这样:
output/images/
├── bcm2710-rpi-3-b.dtb # 设备树文件
├── boot.vfat # boot 分区镜像(FAT32)
├── rootfs.ext2 # rootfs 镜像(ext2格式)
├── rootfs.ext4 # rootfs 镜像(ext4格式)
├── rootfs.tar # rootfs 打包文件
├── sdcard.img # 完整的 SD 卡镜像(一键烧录)
├── u-boot.bin # U-Boot 引导程序
├── zImage # 压缩后的内核镜像
└── rpi-firmware/ # 树莓派专用固件
我习惯把这些文件分成三类:
| 类别 | 文件 | 说明 |
|---|---|---|
| Bootloader | u-boot.bin、rpi-firmware/ | 引导程序,负责加载内核 |
| Kernel | zImage、*.dtb | Linux 内核和设备树 |
| Rootfs | rootfs.ext4、rootfs.tar | 根文件系统 |
你想想看,嵌入式系统启动就三步:Bootloader 跑起来 → 加载内核 → 挂载根文件系统。这三个文件正好对应这三个阶段。
4.3 各镜像文件深度解析
4.3.1 Bootloader:u-boot.bin
U-Boot 是嵌入式 Linux 最常用的引导程序。它负责初始化硬件、加载内核到内存、然后跳转执行。
我在项目中遇到过一个问题:U-Boot 编译出来有 500KB,但板子的 SPI Flash 只有 256KB。怎么办?
两种思路:
- 裁剪 U-Boot:去掉不需要的驱动和命令
- 改用 SPL:分两阶段加载,SPL 只做最小初始化
Buildroot 里可以通过配置 BR2_TARGET_UBOOT_SPL=y 来启用 SPL 模式。
4.3.2 Kernel:zImage + DTB
zImage 是自解压的内核镜像。DTB(设备树)是描述硬件信息的二进制文件。
为什么内核和硬件信息要分开?
因为同一份内核可以跑在不同板子上,只要换 DTB 文件就行。我维护过 5 款不同硬件,共用同一份内核镜像,只换 DTB,省了不少事。
小技巧:查看 DTB 里的硬件信息,可以用 dtc -I dtb -O dts xxx.dtb 反编译成可读的 DTS 文件。
4.3.3 Rootfs:rootfs.ext4 / rootfs.tar
根文件系统是整个系统的灵魂。Buildroot 支持多种格式:
- ext2/ext4:适合 SD 卡、eMMC 等块设备
- squashfs:只读压缩文件系统,适合 Flash
- jffs2/ubifs:专门针对 NAND Flash 设计的文件系统
- tar:打包格式,方便解压到已有分区
我个人习惯:开发阶段用 ext4,方便调试;量产阶段用 squashfs,防篡改还省空间。
4.4 SD 卡烧录实战
好,理论说完了,咱们直接上手烧录。
4.4.1 方法一:使用 sdcard.img 一键烧录
如果你在 Buildroot 配置中启用了 BR2_PACKAGE_HOST_GENIMAGE 和 BR2_ROOTFS_POST_IMAGE_SCRIPT,编译完成后会自动生成一个完整的 sdcard.img。
烧录命令就一行:
# 假设 SD 卡设备是 /dev/sdb(注意!别搞错盘符)
sudo dd if=output/images/sdcard.img of=/dev/sdb bs=4M status=progress
sync
警告:我曾经手滑把 /dev/sdb 写成了 /dev/sda,结果把电脑硬盘给覆盖了。血的教训!烧录前一定用 lsblk 确认设备名。
4.4.2 方法二:手动分区烧录
有时候你需要自定义分区布局,那就手动来:
# 1. 分区(以 2GB SD 卡为例)
sudo fdisk /dev/sdb
# 创建两个分区:
# /dev/sdb1: 64MB, FAT32, 类型 c (boot)
# /dev/sdb2: 剩余空间, Linux, 类型 83 (rootfs)
# 2. 格式化
sudo mkfs.vfat /dev/sdb1
sudo mkfs.ext4 /dev/sdb2
# 3. 烧录 boot 分区
sudo mount /dev/sdb1 /mnt/boot
sudo cp output/images/zImage /mnt/boot/
sudo cp output/images/*.dtb /mnt/boot/
sudo cp output/images/u-boot.bin /mnt/boot/
sudo umount /mnt/boot
# 4. 烧录 rootfs 分区
sudo mount /dev/sdb2 /mnt/rootfs
sudo tar -xvf output/images/rootfs.tar -C /mnt/rootfs
sudo umount /mnt/rootfs
手动分区的好处是灵活。比如 boot 分区你想用 ext4 而不是 FAT32?没问题,自己改就行。
4.4.3 验证烧录结果
烧录完别急着拔卡,先验证一下:
# 检查分区表
sudo fdisk -l /dev/sdb
# 检查 boot 分区内容
sudo mount /dev/sdb1 /mnt
ls /mnt
# 应该能看到 zImage、*.dtb、u-boot.bin 等文件
sudo umount /mnt
# 检查 rootfs 分区
sudo mount /dev/sdb2 /mnt
ls /mnt
# 应该能看到 bin、etc、lib、usr 等目录
sudo umount /mnt
插到板子上,上电,看串口输出。如果看到 U-Boot 启动信息,然后内核开始解压,最后出现登录提示符...恭喜你,系统跑起来了!
4.5 本章知识体系
下面这张图总结了 output 目录的核心逻辑:
从这张图可以看得很清楚:Buildroot 编译完成后,output/images/ 里产出三类核心文件,最终通过烧录工具写入 SD 卡。板子上电后,Bootloader → Kernel → Rootfs 依次加载,系统就起来了。
我的建议:刚开始学的时候,先用 sdcard.img 一键烧录,跑通了再尝试手动分区。这样能快速建立信心,也方便排查问题。
好了,output 目录和烧录方法就讲到这里。记住一句话:images 目录是终点,其他目录是过程。下次编译完,直接去 images 里找你要的文件就行。
公众号:蓝海资料掘金营,微信deep3321