4、根文件系统构建:Busybox制作最小根文件系统、Yocto/OpenEmbedded构建实战、Buildroot快速入门

说到根文件系统,很多刚入行的朋友会觉得它很神秘。其实说白了,它就是Linux启动后挂载的第一个文件系统,里面装着init程序、shell、各种库和配置文件。没有它,内核启动后就是个光杆司令,啥也干不了。

我在座舱项目里折腾根文件系统好几年了,踩过的坑能写满一个笔记本。今天咱们就聊聊三种主流的构建方式:Busybox、Yocto和Buildroot。每种都有它的脾气,选对了事半功倍。

4.1 Busybox:最小根文件系统的瑞士军刀

Busybox是什么?它把几百个Linux命令打包成一个可执行文件。你想想看,一个ls命令、一个cp命令、一个ps命令,每个单独编译都要几十KB。Busybox把它们合在一起,整个才几百KB。对于嵌入式设备来说,这简直是神器。

我习惯用Busybox做最小系统,特别是项目早期验证阶段。为什么?因为快啊!从配置到生成根文件系统,熟练的话十分钟搞定。

4.1.1 编译Busybox

先下载源码,然后配置。我个人习惯用默认配置,再手动调整几个关键选项:

# 下载Busybox
wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar -xjf busybox-1.36.0.tar.bz2
cd busybox-1.36.0

# 配置
make menuconfig

# 关键配置项:
# -> Settings -> Build static binary (no shared libs) [建议不选,除非你清楚后果]
# -> Settings -> Cross compiler prefix [设置交叉编译工具链前缀]
# -> Linux System Utilities -> mount [确保选中,座舱系统需要挂载各种设备]

嗯,这里要注意:静态编译看起来省事,但实际项目中我吃过亏。静态编译的busybox体积大,而且有些功能依赖动态库反而更灵活。除非你的系统小到极致,否则我建议用动态编译。

# 编译和安装
make -j4
make install CONFIG_PREFIX=../rootfs

执行完上面命令,../rootfs目录下就生成了bin、sbin、usr等目录。但别高兴太早,这还只是个空壳子。

4.1.2 构建完整的根文件系统

Busybox只提供了命令,你还需要手动添加以下内容:

组件 说明 我的建议
/dev 设备节点 用devtmpfs自动管理,别手动创建了
/etc 配置文件 至少要有inittab、fstab、passwd
/lib 动态库 从交叉编译工具链里拷贝
/proc 进程信息 挂载点,启动时mount即可
/sys 内核信息 同上

我曾经在某个项目中,忘了拷贝libc库,结果系统启动后所有命令都报"file not found"。排查了半天,最后发现是动态链接器没找到。从那以后,我每次构建完都会用readelf -d检查一下依赖。

# 检查busybox依赖了哪些库
readelf -d ../rootfs/bin/busybox | grep NEEDED

# 从工具链拷贝库
cp -r /path/to/toolchain/arm-linux-gnueabihf/libc/lib/* ../rootfs/lib/
小技巧:ldd命令可以列出所有依赖,但注意要在目标架构上运行。交叉编译环境下,我习惯用arm-linux-gnueabihf-readelf来检查。

4.1.3 制作根文件系统镜像

最后一步,把目录打包成镜像。座舱系统常用的是ext4格式:

# 创建空镜像文件
dd if=/dev/zero of=rootfs.ext4 bs=1M count=64

# 格式化为ext4
mkfs.ext4 rootfs.ext4

# 挂载并拷贝文件
mount -o loop rootfs.ext4 /mnt
cp -r ../rootfs/* /mnt/
umount /mnt

64MB够用吗?对于最小系统来说够了。但座舱系统通常要跑QT、Wayland这些,我建议至少256MB起步。

4.2 Yocto/OpenEmbedded:工业级构建系统

如果你觉得Busybox太原始,那Yocto就是另一个极端。它强大到令人发指,也复杂到令人发指。

我第一次接触Yocto是在一个量产项目中。客户要求定制化Linux系统,要支持OTA升级、安全启动、多分区管理。用Busybox手动搞?那得累死。Yocto的layer机制和recipe系统,正好解决这些问题。

4.2.1 Yocto的核心概念

Yocto不是工具,是一个框架。它包含:

  • Poky:参考发行版,Yocto的起点
  • BitBake:构建引擎,类似make但更强大
  • Recipe:构建配方,描述怎么编译一个软件
  • Layer:层,用于组织recipe和配置

你想想看,一个完整的Linux系统有成百上千个软件包。每个包都有依赖关系、编译选项、补丁。Yocto用recipe把这些都管理起来,而且可以精确控制每个包的版本。

4.2.2 搭建Yocto环境

说实话,Yocto的第一次构建很劝退。我当年在笔记本上跑,第一次构建花了6个小时。所以建议你用一台性能好的服务器,或者至少给虚拟机分配8核CPU和16GB内存。

# 安装依赖
sudo apt-get install gawk wget git diffstat unzip texinfo gcc \
  build-essential chrpath socat cpio python3 python3-pip \
  python3-pexpect xz-utils debianutils iputils-ping python3-git \
  python3-jinja2 libegl1-mesa libsdl1.2-dev xterm

# 下载Poky
git clone git://git.yoctoproject.org/poky
cd poky

# 初始化构建环境
source oe-init-build-env build

执行完source命令后,你会进入build目录。这时候修改conf/local.conf

# 指定目标机器,座舱系统常用qemuarm64或具体的开发板
MACHINE ??= "qemuarm64"

# 启用一些有用的功能
PACKAGE_CLASSES ?= "package_rpm"
EXTRA_IMAGE_FEATURES ?= "debug-tweaks"

# 添加你需要的软件包
IMAGE_INSTALL_append = " openssh strace gdb"
注意:Yocto默认会从网络下载源码。如果你的开发环境不能联网,需要提前配置本地镜像源。我曾经在客户现场搞了三天,就是因为网络不通,最后用移动硬盘拷了20GB的源码包才搞定。

4.2.3 构建和定制

配置好之后,执行构建命令:

bitbake core-image-minimal

这个命令会编译一个最小系统。如果你想加座舱相关的组件,比如Wayland、Weston、QT,可以用:

bitbake core-image-weston

Yocto的layer机制非常灵活。比如你要支持某款座舱芯片,只需要添加对应的BSP layer:

# 在bblayers.conf中添加
BBLAYERS += " /path/to/meta-xxx-bsp "

我个人习惯把自定义配置放在单独的meta层里,这样方便复用和版本管理。比如meta-cockpit层,里面放座舱特有的应用、配置和服务。

4.3 Buildroot:平衡之道

如果你觉得Busybox太简陋,Yocto又太重,那Buildroot就是中间的那个选择。它比Busybox完善,比Yocto简单。

我在几个中型项目中用过Buildroot,感觉它特别适合那种"不想折腾太多,但又需要一定定制性"的场景。

4.3.1 Buildroot快速上手

Buildroot的使用流程非常直观:

# 下载
wget https://buildroot.org/downloads/buildroot-2023.02.tar.gz
tar -xzf buildroot-2023.02.tar.gz
cd buildroot-2023.02

# 配置
make menuconfig

# 关键配置:
# Target options -> Target Architecture [选择ARM或AArch64]
# Toolchain -> Toolchain type [External toolchain,用你已有的]
# System configuration -> Root filesystem overlay directories [添加你的自定义文件]
# Target packages -> 选择需要的软件包

Buildroot的菜单配置比Busybox丰富得多。它内置了上千个软件包的配置选项,从busybox到QT、从dropbear到nginx,勾选就能编译。

4.3.2 构建和输出

配置完成后,执行:

make

Buildroot会下载源码、交叉编译、打包,一气呵成。最终输出在output/images目录下:

文件 说明
rootfs.tar 根文件系统压缩包
rootfs.ext4 ext4格式镜像
zImage 内核镜像(如果选了内核编译)
sdcard.img 完整的SD卡镜像

我记得有一次,客户要求系统启动时间控制在3秒以内。我用Buildroot做了极致裁剪,去掉所有不必要的服务,最终启动时间降到了2.8秒。Buildroot的make graph-depends命令帮了大忙,它能可视化显示包依赖关系,方便你找出哪些是可以砍掉的。

4.3.3 自定义脚本和overlay

Buildroot支持两种自定义方式:

  • Rootfs overlay:把你的文件放在board/xxx/rootfs-overlay/目录下,构建时会自动覆盖到根文件系统
  • Post-build脚本:在构建完成后执行自定义脚本,比如修改配置文件、设置权限
# 在menuconfig中配置
System configuration -> Root filesystem overlay directories
  -> board/myboard/rootfs-overlay

System configuration -> Custom scripts to run after building filesystems
  -> board/myboard/post-build.sh
我的习惯:把座舱应用的配置文件、启动脚本、测试工具都放在overlay里。这样Buildroot只负责系统层面,应用层面由overlay管理,职责清晰。

4.4 三种方案对比

说了这么多,到底该选哪个?我根据自己的经验总结了一张表:

维度 Busybox Buildroot Yocto
学习曲线
构建时间 分钟级 小时级 小时~天级
定制性
包管理 内置 BitBake
适合场景 原型验证、极小系统 中小型产品 大型量产项目

我的建议是:

  • 如果你刚开始学嵌入式Linux,或者只是做个demo,用Busybox
  • 如果你的产品功能中等,团队不大,用Buildroot
  • 如果你在做量产座舱产品,需要OTA、安全、多平台支持,那就老老实实用Yocto

最后说一句,不管用哪种方案,根文件系统构建完成后一定要做两件事:一是用QEMU模拟启动测试,二是在真实硬件上跑一遍。我见过太多人在QEMU上跑得好好的,一上真机就翻车。嗯,今天就聊到这里,下节课咱们聊聊根文件系统的启动优化。