3. Busybox 编译与配置:从源码到根文件系统的第一步
说实话,Busybox 是整个 rootfs 的基石。你想想看,一个只有 2MB 左右的二进制文件,却能模拟出上百个 Linux 标准命令——这就是嵌入式系统的魅力所在。我最早接触 Busybox 是在一个存储资源极度受限的 MIPS 路由器上,那时候我才真正理解什么叫「麻雀虽小,五脏俱全」。
这一节,我们就来亲手走一遍 Busybox 的下载、配置和编译流程。我会把那些文档里没写的坑,一个一个给你指出来。
3.1 下载 Busybox 源码
我个人习惯从官方站点获取源码,而不是用发行版仓库里的版本。为什么?因为官方源码最干净,没有杂七杂八的补丁干扰。
# 下载最新稳定版(以 1.36.1 为例)
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
# 解压
tar -xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
解压后,目录结构大致如下:
| 目录/文件 | 说明 |
|---|---|
| applets/ | Busybox 主入口和 applet 注册逻辑 |
| libbb/ | 公共库函数,比如字符串操作、文件 I/O |
| networking/ | 网络相关命令(ping、ifconfig、telnetd 等) |
| util-linux/ | Linux 工具类命令(mount、fdisk 等) |
| Config.in | Kconfig 配置文件,menuconfig 的菜单定义 |
| .config | 编译配置结果文件(menuconfig 生成) |
3.2 menuconfig 配置:把不需要的统统砍掉
配置 Busybox 的核心就是 make menuconfig。这个界面和 Linux 内核的配置界面同源,如果你配过内核,上手毫无压力。
make menuconfig
进去之后,你会看到一堆分类菜单。我一般按这个顺序来:
- Settings → Build static binary (no shared libs) —— 静态编译开关,后面细说
- Settings → Cross Compiler prefix —— 填你的交叉编译工具链前缀,比如
arm-linux-gnueabihf- - Coreutils / Shells / Networking / ... —— 逐个勾选你需要的命令
我举个例子。有一次我做工业控制器的 rootfs,设备只需要跑一个监控进程和网络通信。最后我选中的命令只有 28 个:
- 必备: init、sh、mount、umount、cat、echo、ls、cp、mv、rm、mkdir、chmod、chown
- 网络: ifconfig、ping、telnetd、udhcpc
- 调试: ps、top、free、dmesg、kill、grep、sed、awk
- 系统: reboot、poweroff、hwclock
其他的,比如 vi、ftpget、httpd,统统不选。最终 busybox 二进制只有 680KB,比全功能版本小了将近一半。
3.3 静态编译 vs 动态编译:怎么选?
这是每个做 rootfs 的人都会纠结的问题。我直接给你结论:
| 对比项 | 静态编译 | 动态编译 |
|---|---|---|
| 二进制大小 | 约 1.5-2.5 MB | 约 500-900 KB |
| 依赖 | 无外部依赖 | 需要 libc.so、ld-linux.so 等 |
| 启动速度 | 稍快(省去动态链接时间) | 稍慢(需要加载共享库) |
| 升级维护 | 升级 busybox 需整体替换 | 可单独升级 libc 或 busybox |
| 适用场景 | 极小系统、initramfs、单板 | 有完整文件系统的嵌入式 Linux |
我的建议:
- 如果你做的是 initramfs 或者 极小系统(flash < 8MB),用静态编译。省掉 libc 的 1-2MB 空间,很划算。
- 如果你的 rootfs 已经有 glibc 或 musl,用动态编译。这样 busybox 本身更小,而且 libc 的 bug 修复可以独立进行。
静态编译的配置方法很简单:
make menuconfig
# 进入 Settings → Build static binary (no shared libs)
# 按 Y 选中
# 保存退出
动态编译则保持该项为 N,但你需要确保 rootfs 里有正确的 libc 和动态链接器。路径一般是 /lib/libc.so.6 和 /lib/ld-linux.so.3(ARM 平台)。
readelf -d busybox | grep NEEDED 检查依赖。
3.4 编译与验证
配置完成后,执行编译:
# 清理旧编译产物(第一次可跳过)
make clean
# 编译
make -j$(nproc)
# 安装到指定目录
make install CONFIG_PREFIX=./_install
编译完成后,_install 目录下会出现 bin/、sbin/、usr/ 等目录,里面是 busybox 的符号链接。你可以用 file 命令确认编译类型:
file _install/bin/busybox
# 静态编译输出示例:
# busybox: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked
# 动态编译输出示例:
# busybox: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked
我习惯再用 size 命令看一眼各段的大小:
arm-linux-gnueabihf-size _install/bin/busybox
# 输出示例:
# text data bss dec hex filename
# 654321 12345 6789 673455 a45ef busybox
text 是代码段,data 是已初始化数据,bss 是未初始化数据。这三个值加起来,就是 busybox 在内存中的占用。你可以根据这个值评估你的 rootfs 是否放得下。
3.5 知识体系总览
下面这张图把 Busybox 编译配置的核心流程串起来了:
3.6 几个实用技巧
- 保存配置: 配好 menuconfig 后,把
.config文件备份到版本库。下次直接cp .config.bak .config && make oldconfig,省得重新点菜单。 - 查看具体命令大小: 用
make V=1可以看详细编译日志,但更实用的方法是编译后用du -h _install/bin/busybox看整体大小。 - 交叉编译: 别忘了在 menuconfig 里设置
Cross Compiler prefix,或者在命令行传参:make CROSS_COMPILE=arm-linux-gnueabihf-。
file 命令一看,发现是 x86 的 ELF,那就尴尬了。
好了,Busybox 的编译配置就讲到这里。你拿到一个能用的 busybox 二进制之后,下一步就是把它放到 rootfs 里,配上 init 脚本和必要的设备节点。这些内容我们后面会详细展开。