3、文件系统裁剪(上):BusyBox的配置与裁剪、移除不必要的命令、动态链接库的依赖分析(ldd命令)、strip工具的使用
文件系统裁剪,说白了就是给你的嵌入式设备「减肥」。我见过太多新手,上来就把整个Ubuntu根文件系统塞进去,结果Flash空间直接爆掉。嗯,今天我们就聊聊怎么用BusyBox、ldd和strip这三板斧,把文件系统从几百兆砍到几兆。
3.1 BusyBox的配置与裁剪
BusyBox是什么?它就是个「瑞士军刀」。一个二进制文件,集成了两百多个Linux命令。你想想看,原来每个命令都要一个独立的可执行文件,现在全塞进一个文件里,空间省了多少?
我个人习惯,拿到BusyBox源码后第一件事不是编译,而是先跑一遍make menuconfig。这里有个坑——默认配置会编译几乎所有命令,很多你用不上。
核心思路:只保留你需要的命令,其他的统统砍掉。
举个例子,如果你的嵌入式设备不需要网络功能,那ifconfig、ping、route这些命令就可以去掉。不需要打印服务?lpd、lpq这些也砍掉。
我曾经在一个IoT项目里,把BusyBox从默认的2.3MB裁剪到了680KB。怎么做到的?
- 按功能模块裁剪:在
Settings → Build Options里,选择静态编译还是动态编译。我建议用动态编译,能省不少空间。 - 按命令裁剪:进入各个子菜单,把不需要的命令取消勾选。比如
Coreutils里,cat、echo、ls这些基本命令保留,factor、hostid这种冷门命令直接干掉。 - 按功能特性裁剪:有些命令支持很多选项,比如
ls支持-l、-a、-R等。如果你只需要基本功能,可以在ls的配置里关掉不必要的选项。
小技巧:配置完成后,运行make编译,然后make install安装到指定目录。记得用du -sh看看最终大小,对比一下裁剪前后的差异。
3.2 移除不必要的命令
BusyBox裁剪完了,但你的文件系统里可能还残留着一些「垃圾」。比如/usr/bin、/bin、/sbin这些目录下,可能还有原来系统自带的命令。
我建议的做法是:
- 先清空:把
/bin、/sbin、/usr/bin、/usr/sbin这些目录清空,只保留BusyBox的符号链接。 - 再重建:用BusyBox的
--install选项,自动创建所有需要的符号链接。
# 清空原有命令
rm -rf /bin/*
rm -rf /sbin/*
rm -rf /usr/bin/*
rm -rf /usr/sbin/*
# 用BusyBox重建符号链接
busybox --install -s /bin
busybox --install -s /sbin
busybox --install -s /usr/bin
busybox --install -s /usr/sbin
这样做的好处是,所有命令都指向同一个BusyBox二进制文件。你想想看,原来每个命令占4KB,200个命令就是800KB。现在全指向一个文件,省了多少?
注意:有些命令可能依赖特定的路径或权限。比如mount需要setuid权限,记得检查一下。
3.3 动态链接库的依赖分析(ldd命令)
好了,BusyBox搞定了。但你的程序可能还依赖一堆动态链接库。这些库文件往往比BusyBox本身还大。怎么找出哪些库是真正需要的?用ldd。
ldd命令会列出可执行文件依赖的所有动态库。比如:
$ ldd /bin/busybox
linux-vdso.so.1 (0x00007ffe3b9f0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a1a200000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8a1a000000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8a1a400000)
看到没?BusyBox只依赖libc、libm和ld-linux这三个库。但如果你有其他程序,比如网络服务、数据库等,依赖的库就多了。
我曾在项目中遇到一个问题:一个程序在开发板上跑不起来,报错cannot open shared object file。一查,原来是libpthread.so.0没拷贝进去。用ldd一查就发现了。
实际操作中,我建议写个脚本,递归分析所有可执行文件的依赖:
#!/bin/bash
# 分析所有可执行文件的依赖库
TARGET_DIR=/path/to/rootfs
LIBS=""
for file in $(find $TARGET_DIR -type f -executable); do
deps=$(ldd $file 2>/dev/null | grep "=> /" | awk '{print $3}')
LIBS="$LIBS $deps"
done
# 去重并拷贝
for lib in $(echo $LIBS | tr ' ' '\n' | sort -u); do
cp -v $lib $TARGET_DIR/lib/
done
关键点:只拷贝真正需要的库,不要整个/lib目录都搬过去。我见过有人直接把Ubuntu的/lib拷过去,结果一个库都没少,文件系统直接膨胀到200MB。
3.4 strip工具的使用
最后一步,用strip给二进制文件和库「瘦身」。什么是strip?它会把可执行文件里的符号表、调试信息等运行时不需要的东西去掉。
你想想看,一个编译出来的程序,里面包含了函数名、变量名、行号信息等。这些在调试时有用,但运行时完全不需要。去掉之后,文件大小能减少30%到50%。
我习惯在构建脚本的最后加一行:
# 对目标目录下的所有可执行文件和库进行strip
find /path/to/rootfs -type f \( -executable -o -name "*.so*" \) -exec strip --strip-unneeded {} \;
这里有个细节:--strip-unneeded比-s更安全。它只去掉不必要的符号,不会影响程序的正常运行。我曾经用-s把某个库的符号全删了,结果程序启动时直接崩溃。后来改用--strip-unneeded就没事了。
经验之谈:strip之后,记得用file命令检查一下,确认文件类型还是ELF,而不是变成了data。如果变成data,说明strip过头了,这个文件可能已经损坏。
3.5 本章知识体系
下面这张图,是我梳理的文件系统裁剪核心流程。你可以对照着看,每一步都对应我们刚才讲的内容。
这张图把整个流程串起来了。从BusyBox配置开始,到移除多余命令,再到动态库分析,最后用strip瘦身。每一步都有明确的工具和方法。
好了,文件系统裁剪的上半部分就讲到这里。记住三个核心点:BusyBox按需配置、ldd精准分析依赖、strip安全去符号。这三招用好了,你的文件系统至少能瘦一半。
总结:裁剪不是目的,目的是让嵌入式系统跑得更快、更稳。空间省下来了,系统启动时间也会缩短。我见过一个项目,裁剪后系统启动时间从15秒降到了3秒——因为文件系统小了,加载的东西少了。
可可精品资料库