第2章:Busybox编译实战
好,咱们直接进入正题。上一章聊了Busybox是什么,这一章咱们动手干——把Busybox编译出来,跑在目标板上。
说实话,编译这事儿看着简单,但坑不少。我刚开始做嵌入式那会儿,光交叉编译环境就折腾了两天。嗯,咱们一步步来。
2.1 交叉编译环境搭建
先说说什么是交叉编译。你想想看,你的PC是x86架构,目标板可能是ARM、MIPS或者RISC-V。PC上编译出来的程序,要在目标板上跑——这就是交叉编译。
搭建环境,我习惯用这套工具链:
# 下载ARM交叉编译工具链
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz
# 解压到指定目录
tar -xf gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz -C /opt/
# 添加环境变量
export PATH=/opt/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
验证工具链是否装好:
arm-none-linux-gnueabihf-gcc --version
# 输出类似:
# arm-none-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 10.3-2021.07) 10.3.1 20210621
看到版本号,说明环境搭好了。如果提示"command not found",检查一下路径写对了没。
2.2 获取Busybox源码
源码从官网下载就行,我一般用稳定版:
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -jxf busybox-1.36.1.tar.bz2
cd busybox-1.36.1/
解压后看看目录结构:
ls -l
# 你会看到:
# applets/ # 主入口
# coreutils/ # 核心工具(cp, mv, rm等)
# networking/ # 网络工具(ping, ifconfig等)
# shell/ # shell相关(ash, hush等)
# util-linux/ # 实用工具(mount, fdisk等)
# Config.in # 配置菜单
# Makefile # 编译脚本
每个目录对应一类工具。你想想看,这其实就是个微型Linux用户态的全家桶。
2.3 menuconfig配置
配置Busybox,用的是经典的menuconfig界面:
make menuconfig
进去之后,你会看到类似Linux内核配置的界面。这里我挑几个关键配置说说:
| 配置项 | 路径 | 说明 |
|---|---|---|
| Build static binary | Settings → Build static binary | 静态编译,不依赖动态库 |
| Cross compiler prefix | Settings → Cross compiler prefix | 填 arm-none-linux-gnueabihf- |
| Path to sysroot | Settings → Path to sysroot | 工具链的sysroot路径 |
| Enable busybox | Busybox Settings → General | 确保选中,否则编译出来是空的 |
配置完保存退出。配置文件会写入 .config,你可以看看:
grep "CROSS" .config
# 输出:CONFIG_CROSS_COMPILER_PREFIX="arm-none-linux-gnueabihf-"
2.4 静态编译 vs 动态编译
这是个经典选择题。我直接说结论:
- 静态编译:所有库都打进二进制文件里。文件大,但移植方便。
- 动态编译:依赖目标板上的动态库。文件小,但库版本要对得上。
怎么选?我个人的经验是:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 开发调试阶段 | 动态编译 | 改代码快,不用每次都重新链接库 |
| 产品发布 | 静态编译 | 避免目标板库版本不一致导致崩溃 |
| 资源受限设备 | 动态编译 | 节省Flash空间,毕竟静态编译可能多出几百KB |
| 最小系统构建 | 静态编译 | 根文件系统里可能没有libc,静态编译省事 |
静态编译的配置方法:
make menuconfig
# 进入 Settings → Build static binary (选中)
# 保存退出
动态编译的话,取消勾选就行。但要注意,目标板上必须有对应的动态库:
# 查看动态库依赖
arm-none-linux-gnueabihf-readelf -d busybox | grep NEEDED
# 输出类似:
# 0x00000001 (NEEDED) libc.so.6
# 0x00000001 (NEEDED) libm.so.6
如果目标板上没有 libc.so.6,动态编译的busybox跑不起来。嗯,这就是为什么很多嵌入式系统直接用静态编译——省心。
2.5 开始编译
配置好了,直接make:
make -j$(nproc)
编译完成后,看看成果:
ls -lh busybox
# 静态编译:-rwxr-xr-x 1 root root 1.2M busybox
# 动态编译:-rwxr-xr-x 1 root root 780K busybox
静态编译比动态大了将近一倍。但好处是,这个文件拷到任何ARM Linux板上都能跑。
验证一下:
file busybox
# 输出:busybox: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked
看到 "statically linked" 或者 "dynamically linked",就知道编译方式对不对了。
2.6 避坑指南
编译过程中可能遇到几个常见问题,我列一下:
- 找不到编译器:检查 PATH 和 CROSS_COMPILE 变量。我习惯在Makefile里直接写死:
CROSS_COMPILE ?= arm-none-linux-gnueabihf- - 静态编译失败:有些工具链的静态库没装全。用
apt install libc6-dev-armhf-cross补一下。 - 配置丢失:make clean 会删掉 .config。我一般备份一份:
cp .config config_arm_static - 版本不匹配:Busybox 1.36 需要较新的工具链。如果工具链太老,降级Busybox版本或者升级工具链。
- 交叉编译环境:工具链 + 环境变量
- menuconfig配置:交叉编译器前缀、静态/动态选择
- 静态编译:文件大,移植性好
- 动态编译:文件小,依赖目标板库
- 编译验证:file命令检查链接方式
好了,这一章就到这儿。下一章咱们聊聊怎么把编译好的busybox集成到根文件系统里,让它真正跑起来。到时候你会看到,busybox只是第一步,后面还有一堆活儿等着呢。