3. rootfs 构建工具链:四大主流方案的流程对比

嵌入式 Linux 的 rootfs 构建,说白了就是「把一堆零散的二进制文件、库、脚本,按照特定结构打包成一个可启动的文件系统」。市面上主流的工具有四个:BusyBox、Buildroot、Yocto、OpenWrt。每个我都用过,踩过不少坑,今天把它们的构建流程掰开揉碎讲清楚。

核心观点:没有最好的工具,只有最适合你项目的工具。选错了,后面改起来会非常痛苦。

3.1 先看一张总览图

下面这张 SVG 图,把四个工具的构建流程和核心差异画出来了。我建议你先看一遍图,再往下读细节。

四大 rootfs 构建工具流程对比 BusyBox 下载源码 + 配置 make menuconfig make 编译 手动组装 rootfs 打包成镜像 Buildroot 下载源码 + 配置 make menuconfig make 全自动编译 自动生成 rootfs 打包成镜像 Yocto 下载 Poky + 配置 编写 layer/recipe bitbake 编译 生成 rootfs + SDK 打包成镜像 OpenWrt 下载 SDK + feeds make menuconfig make V=s 编译 生成固件 + rootfs 打包成固件镜像 说明:BusyBox 需要手动组装 rootfs;Buildroot 全自动生成;Yocto 生成 rootfs + SDK;OpenWrt 面向路由器固件 箭头表示流程走向,颜色区分不同阶段:配置 → 编译 → 生成 → 打包

3.2 BusyBox:最原始的手工打造

BusyBox 本身只是一个精简的 shell 工具集,它不负责构建完整的 rootfs。你需要自己动手做很多事情。

典型流程:

  1. 下载 BusyBox 源码,执行 make menuconfig 选择需要的命令
  2. 编译出 busybox 可执行文件
  3. 手动创建 /bin、/sbin、/usr、/lib、/etc 等目录
  4. 从交叉编译工具链中复制 ld-linux.so.*、libc.so.* 等动态库
  5. 创建 /dev 下的设备节点(或者用 devtmpfs)
  6. 编写 /etc/inittab、/etc/fstab、/etc/init.d/rcS 等初始化脚本
  7. 用 cpio 或 mkfs.ext2 打包成镜像

我的经验:BusyBox 适合学习 rootfs 内部结构,但不适合产品开发。我曾经在一个小项目里用 BusyBox 手动搭 rootfs,结果因为漏了一个库文件,系统启动到一半就 panic 了。排查了整整一个下午。

3.3 Buildroot:一键生成,省心省力

Buildroot 是我个人最常用的工具。它把 BusyBox、uClibc/musl/glibc、各种第三方库、内核、bootloader 全部整合到一个构建系统里。

核心流程:

  • 配置阶段:make menuconfig 选择目标架构、工具链类型、文件系统格式(squashfs、ext4、cpio 等)
  • 下载阶段:自动下载所有选中的软件包源码
  • 编译阶段:按依赖顺序编译工具链、内核、BusyBox、第三方库
  • 组装阶段:自动创建目录结构、复制文件、生成设备节点、生成初始化脚本
  • 打包阶段:生成 rootfs 镜像文件
# Buildroot 典型用法
$ make qemu_arm_vexpress_defconfig
$ make -j4
# 输出在 output/images/rootfs.ext4

注意:Buildroot 的 make clean 会删除整个 output/build 目录,下次编译会重新下载所有源码。我建议用 make clean-target 只清理目标文件系统,保留编译缓存。

3.4 Yocto:企业级定制,但学习曲线陡峭

Yocto 是四个工具里最复杂的,也是功能最强大的。它基于 OpenEmbedded 构建系统,使用 BitBake 作为任务调度引擎。

构建流程:

  1. 下载 Poky(Yocto 的参考发行版)
  2. 设置环境变量:source oe-init-build-env
  3. 编辑 conf/local.conf 指定 MACHINE、DISTRO、PACKAGE_CLASSES
  4. 编写或继承 layer,在 recipe 中描述软件包的源码位置、补丁、依赖、安装路径
  5. 执行 bitbake core-image-minimal 开始构建
  6. BitBake 解析所有 recipe,生成依赖树,并行执行 fetch、patch、configure、compile、install、package 等任务
  7. 最终生成 rootfs、内核、设备树、SDK

关键差异:Yocto 的 rootfs 构建不是简单的文件复制,而是通过 package 管理系统(rpm/deb/ipk)来安装软件包。这意味着你可以精确控制每个文件的属主、权限、依赖关系。

我记得第一次用 Yocto 时,bitbake core-image-minimal 跑了整整 4 个小时。后来发现是因为网络慢,下载源码包花了大量时间。建议第一次构建时,先手动下载所有源码包放到 downloads 目录下。

3.5 OpenWrt:为网络设备而生

OpenWrt 虽然也是基于 Buildroot 衍生出来的,但它的构建系统已经高度定制化,专门面向路由器、网关等网络设备。

构建流程:

  • 下载 OpenWrt SDK 或主线源码
  • 执行 ./scripts/feeds update -a 更新软件包索引
  • 执行 ./scripts/feeds install -a 安装选中的软件包
  • make menuconfig 选择目标平台、内核模块、文件系统大小、Web 界面(LuCI)等
  • make V=s -j$(nproc) 开始编译
  • 输出固件镜像(包含 rootfs、内核、bootloader)

避坑指南:我曾经在 OpenWrt 上踩过一个坑——默认的 rootfs 是 squashfs,只读的。如果你需要运行时写入文件,必须配置 overlay 文件系统,或者改用 ext4。这个在 make menuconfig 的 Target Images 里设置。

3.6 横向对比:一张表说清楚

特性 BusyBox Buildroot Yocto OpenWrt
构建复杂度 低(手动) 中 高 中
首次构建时间 几分钟 30分钟~2小时 2~8小时 1~3小时
rootfs 大小 极小(可到 1MB) 小~中 中~大 小~中
包管理 无 无(编译时静态选择) 有(rpm/deb/ipk) 有(opkg)
学习曲线 平缓 中等 陡峭 中等
适用场景 学习、极简系统 产品原型、小批量 企业级、大规模定制 路由器、网关
初始化系统 BusyBox init BusyBox init / systemd systemd / sysvinit procd

3.7 如何选择?我的建议

你想想看,如果你的项目只是做一个简单的 IoT 传感器,用 BusyBox 手动搭一个 2MB 的 rootfs 完全够用。但如果你要做一款工业网关,需要支持多种协议栈、Web 管理界面、OTA 升级,那 OpenWrt 或者 Buildroot 更合适。

我个人习惯这样选:

  • 学习阶段:先用 BusyBox 手动搭一遍,理解 rootfs 的每个目录是干什么的
  • 快速原型:Buildroot,一天之内就能跑起来
  • 产品量产:Yocto,虽然慢,但可维护性强,适合团队协作
  • 网络设备:OpenWrt,开箱即用,社区活跃

最后提醒一句:不管你选哪个工具,rootfs 的性能优化一定要从构建阶段就开始考虑。比如文件系统类型的选择(squashfs 只读但压缩率高,ext4 可写但有日志开销)、库文件的裁剪(去掉不需要的 locale、调试符号)、初始化脚本的并行启动等。这些我们会在后面的章节详细展开。