BusyBox——嵌入式Linux中的瑞士军刀

2019-07-12 23:01发布

BusyBox (http://www.busybox.net)将数以百计的常用Unix/Linux命令集成到一个可执行文件中(名为busybox).它体积小巧, 功能却不失强大. 常用Linux命令实现的功能它都能提供. 它甚至还提供了tftp, http服务程序. 尽管少数的Linux命令的某些选项BusyBox没能提供, 但这并不影响它在嵌入式Linux系统中的流行.
使用BusyBox, 在为目标系统所构建的根文件系统中添加应用程序就易如反掌了. 想象一下: 单独得交叉编译每个UNIX/Linux应用程序的工作量是多么恐怖.

BusyBox可以与glibc或uClibc库进行链接编译, 可以采用动态链接或静态链接(配置选项中可选). 即便采用与glibc的静态链接, 最终生成的busybox文件大小也能轻易控制在1MB之内(在配置BusyBox时不要选择不需要的功能).  而采用uClibc动态链接的可执行文件就更小了. 这非常适于存储空间紧张的嵌入式Linux系统. 由此, 有人将BusyBox称为嵌入式系统中的瑞士军刀. 更为形象的比喻是: Linux系统中的单个命令是电路中的分立式元件, 而BusyBox是将它们集成在一起的IC: 功能不变, 体积却大为减小.

本文讨论BusyBox的编译, 安装. 包括安装到本地主机和安装到ARM目标系统中.


配置BusyBox
配置BusyBox和配置Linux内核的方法很类似, 思想也大同小异: 也是基于源码树目录中的.config文件来进行的. 还是推荐使用menuconfig配置工具.

下面的工作都是在解压后的BusyBox源码树根目录中进行的.

$ make defconfig       : 针对大多数用户的默认配置
$ make allnoconfig    : 全不选
$ make allyesconfig   : 全选

一般先make deconfig, 然后再使用menuconfig进行配置.
$ make help 查看BusyBox的make选项帮助.

如同配置Linux内核, 你也可以使用既有的.config(可更改其命名)文件. 可以在menuconfig中加载, 输出配置文件.

下面列出一些值得注意的配置选项:

BusyBox Settings
Build Options:  
Build BusyBox as a static binary (no shared libs) : 将BusyBox动态链接或静态连接.
- Do you want to build BusyBox with a Cross Compiler? : 选择交叉编译器.

Installation Options:
默认地, 运行 make install之后, BusyBox将被安装到./_install目录.

配置根据具体需要来: 不需要的不选.
  1. NFS是肯定要选的, 使用NFS将宿主机的文件系统mount到目标板上, 这是嵌入式Linux程序开发的一个重要方面.
  2. 由于可使用NFS, 能在宿主机上实现的功能就都不需要在目标板上实现了.
  3. Debian Utilities 全不选, Editors全不选, System Logging Utilities全不选. 
  4. 解压缩工具只安装解压工具. 而且只选择一种: bzip2相关的, 以及tar工具(只要求目标板能解压缩tar.bz2文件).
  5. shell使用ash.

编译BusyBox
完成对BusyBox的配置工作后, 就可以编译, 安装它了. BusyBox可用于多种体系结构的CPU. 这里分别介绍用于本机系统的BusyBox和用于ARM目标系统的BusyBox. 另外, BusyBox可与glibc或uClibc动态或静态连接. 下面分情况介绍:

用于本机的BusyBox

用于本机的BusyBox编译过程很简单, 只需运行make就可以了.

交叉编译BusyBox

交叉编译BusyBox与编译用于本地系统的BusyBox大同小异, 唯一的区别是它需要使用交叉编译工具. 另外要注意库的链接方式: 如果目标系统中没有某库, 那么BusyBox应该与该库静态链接. 编译步骤如下:

1, 修改PATH变量:
$ export PATH=<交叉编译工具所在目录>:$PATH

2, 在调用make命令时候要指定TARGET_ARCH和CROSS变量.
$ make TARGET_ARCH=arm CROSS=arm-linux-
新版的BusyBox可以在配置过程中设定交叉编译工具.  这样, 为目标系统编译BusyBox的make命令和为本地主机编译BusyBox没有任何区别!

我这里使用Scratchbox来针对ARM目标系统编译BusyBox. (可参考本blog的:使用Scratchbox来开发嵌入式Linux). 安装scratchbox之后, 可以在对scratchbox进行配置时安装各种交叉编译工具: arm-linux-gcc(与glibc链接), arm-linux-uclibc-gcc(与uclibc链接),  它还可以为本地系统(x86)安装与uclibc链接的gcc. 这样你直接指定相应的编译器既可. 不需要另外安装uClibc库.
为了加快编译速度, 可以修改Makefile, 将CC="$(HOSTCC)"改为CC="ccache $(HOSTCC)". 同时也可使用 $ make -j2 用2个任务来进行编译过程.
实例

下面以几个例子来介绍BusyBox的编译方法:

1, 针对本地主机编译与glib链接的BusyBox
由于GNU/Linux系统中都装有glibc, 所以可以不使用Scratchbox提供的编译器. 而且, 可以将BusyBox配置为与glibc库动态链接. 其他选项均设为默认.

$ make defconfig             ; 默认配置
$ make menuconfig         ; 选择与glibc动态链接
$ make -j2       
$ make install

可以使用ls -l, file命令查看生成的busybox可执行文件相关信息. 注意, 在源码树根目录同时还生成了busybox_unstripped文件, 将它strip之后即得到busybox.

默认配置, 与glibc动态连接后的busybox大小为: 858K

2, 针对本地主机编译与uClibc链接的BusyBox
我没有另外地在本地主机上安装uClibc, 而是使用Scratchbox提供的编译工具: i386-gcc-3.3.2-uclibc-snapshot-20040229. 由于本地主机上没有uClibc库, 所以将busybox与uClibc库静态链接. 其他选项设为默认

$ export PATH= /home/zp/project/sb/scratchbox/compilers/i386-gcc-3.3.2-uclibc-snapshot-20040229/bin :$PATH
$ make menuconfig                             
$ make CROSS=i386-linux-uclibc- -j2   
$ make install        
 
默认配置, 与uClibc静态链接后的busybox大小为: 1.1M

3, 针对ARM目标系统编译与glibc动态连接的BusyBox
使用Scratchbox中提供的arm-linux-gcc交叉编译工具. 这里没有使用默认配置, 而是只选择我认为需要的功能.

$ export PATH=/home/zp/project/sb/scratchbox/compilers/arm-linux-gcc3.4.cs-glibc2.3/bin:$PATH
$ make menuconfig                        
$ make CROSS=arm-linux- -j2  
$ make install     

剔除了不需要的内容, 与glibc动态链接后的busybox大小为: 382K

4, 针对ARM目标系统编译与uClibc静态连接的BusyBox
使用Scratchbox中提供的arm-linux-uclibc-gcc交叉编译工具. 这里没有使用默认配置, 而是只选择我认为需要的功能.

$ export PATH=:/home/zp/project/sb/scratchbox/compilers/arm-linux-gcc3.4.cs-uclibc0.9.27/bin$PATH
$ make menuconfig                       
$ make CROSS=arm-linux-uclibc -j2  
$ make install     

剔除了不需要的内容, 与uClibc静态链接后的busybox大小为: 509K
注意: 针对ARM目标系统编译BusyBox时, 可在menuconfig中选择特定的交叉编译器. 这样就make命令就和为本地主机编译BusyBox一样了, 不需要更改PATH和CROSS变量.


安装, 运行BusyBox
编译完成后, 输入 $ make install 进行BusyBox的安装.

默认地, BusyBox将在当前目录(也就是BusyBox源码树根目录)新建一个名为"_install"的目录. BusyBox将被安装到其中.  可以在配置BusyBox时设定安装目录.

完成安装之后, 在./_install目录中有下列文件和目录:
bin  linuxrc  sbin  usr

其中linuxrc, bin, sbin, usr目录中的包含一些我们熟悉的位于PC机/bin, /sbin, /usr/bin, /usr/sbin目录中的程序. 而BusyBox安装的这些程序都是指向busybox文件(位于./_install/bin目录)自身的符号连接.

运行BusyBox"安装"的这些程序时候, 实际上都是在调用busybox这个单独的程序. 比如: $ ./ls -l , 实际向busybox传递了2个参数: ls 和-l. 前面的命令等价于$ ./busybox ls -l.

针对前面编译实例中安装的BusyBox, 我们分别讨论针对本地主机编译的BusyBox和针对ARM目标系统编译的BusyBox.

本地主机
很简单, 可以直接调用busybox, 也可以调用连接到它的符号连接. 当然, 可以修改PATH变量来直接调用它们.

$ ./busybox ls -l
$ ./ ls -l

ARM目标系统
可以使用仿真工具来运行. 这里使用qemu. 在Debian/Ubuntu中安装qemu: $ sudo apt-get install qemu

$ qemu-arm ./busybox ls -l

这里有个问题, 如果busybox是与C库动态链接的(哪怕是和glibc库动态链接), 那么运行它是将会产生段错误:
Unable to load interpreter
Segmentation fault (core dumped)

如果采用静态链接, 则没有问题.
BusyBox还提供与init类似的功能, 也非常适用与嵌入式系统.