BusyBox 简化嵌入式 Linux 系统

BusyBox是很多标准Linux®工具的一个单个可执行实现。BusyBox包含了一些简单的工具,例如cat和echo,还包含了一些更大、更复杂的工具,例如grep、find、mount以及telnet(不过它的选项比传统的版本要少);有些人将BusyBox称为Linux工具里的瑞士军刀。本文将探索BusyBox的目标,它是如何工作的,以及为什么它对于内存有限的环境来说是如此重要。

BusyBox的诞生

BusyBox最初是由BrucePerens在1996年为DebianGNU/Linux安装盘编写的。其目标是在一张软盘上创建一个可引导的GNU/Linux系统,这可以用作安装盘和急救盘。一张软盘可以保存大约1.4-1.7MB的内容,因此这里没有多少空间留给Linux内核以及相关的用户应用程序使用。

BusyBox许可证
BusyBox是按照GNUGeneralPublicLicense(GPL)许可证发行的。这意味着如果我们在一个项目中使用BusyBox,就必须遵守这个许可证。我们可以在BusyBoxWeb站点(请参看本文后面参考资料一节的内容)上看到这个许可证的内容。BusyBox团队似乎正忙于监视违反这个许可证的情况。实际上,他们维护了一个“HallofShame”页面来说明违反者的情况。

BusyBox揭露了这样一个事实:很多标准Linux工具都可以共享很多共同的元素。例如,很多基于文件的工具(比如grepfind)都需要在目录中搜索文件的代码。当这些工具被合并到一个可执行程序中时,它们就可以共享这些相同的元素,这样可以产生更小的可执行程序。实际上,BusyBox可以将大约3.5MB的工具包装成大约200KB大小。这就为可引导的磁盘和使用Linux的嵌入式设备提供了更多功能。我们可以对2.4和2.6版本的Linux内核使用BusyBox。

BusyBox是如何工作的?

为了让一个可执行程序看起来就像是很多可执行程序一样,BusyBox为传递给C的main函数的参数开发了一个很少使用的特性。回想一下C语言的main函数的定义如下:

POSIX环境
尽管BusyBox的目标是提供一个相当完整的POSIX(可移植操作系统接口)环境,这是一个期望,而不是一种需求。这些工具虽然并不完整,但是它们提供了我们期望的主要功能。

清单1.C的main函数
intmain(intargc,char*argv[])

在这个定义中,argc是传递进来的参数的个数(参数数量),而argv是一个字符串数组,代表从命令行传递进来的参数(参数向量)。argv的索引0是从命令行调用的程序名。

清单2给出的这个简单C程序展示了BusyBox的调用。它只简单地打印argv向量的内容。


清单2.BusyBox使用argv[0]来确定调用哪个应用程序
//test.c
#include<stdio.h>

intmain(intargc,char*argv[])
{
inti;

for(i=0;i<argc;i++){
printf("argv[%d]=%s/n",i,argv[i]);
}

return0;
}

调用这个程序会显示所调用的第一个参数是该程序的名字。我们可以对这个可执行程序重新进行命名,此时再调用就会得到该程序的新名字。另外,我们可以创建一个到可执行程序的符号链接,在执行这个符号链接时,就可以看到这个符号链接的名字。


清单3.在使用新命令更新BusyBox之后的命令测试
___FCKpd___2nbsp;gcc-Wall-otesttest.c
___FCKpd___2nbsp;./testarg1arg2
argv[0]=./test
argv[1]=arg1
argv[2]=arg2

___FCKpd___2nbsp;mvtestnewtest
___FCKpd___2nbsp;./newtestarg1
argv[0]=./newtest
argv[1]=arg1

___FCKpd___2nbsp;ln-snewtestlinktest
___FCKpd___2nbsp;./linktestarg
argv[0]=./linktest
argv[1]=arg

BusyBox使用了符号链接以便使一个可执行程序看起来像很多程序一样。对于BusyBox中包含的每个工具来说,都会这样创建一个符号链接,这样就可以使用这些符号链接来调用BusyBox了。BusyBox然后可以通过argv[0]来调用内部工具。





回页首


配置并编译BusyBox

我们可以从BusyBox的Web站点上下载最新版本的BusyBox(请参看参考资料一节的内容)。与大部分开放源码程序一样,它是以一个压缩的tarball形式发布的,我们可以使用清单4给出的命令将其转换成源代码树。(如果我们下载的版本不是1.1.1,那就请在这个命令中使用适当的版本号以及特定于这个版本号的命令。)


清单4.展开BusyBox
___FCKpd___3nbsp;tarxvfzbusybox-1.1.1.tar.gz
$

结果会生成一个目录,名为busybox-1.1.1,其中包含了BusyBox的源代码。要编译默认的配置(其中包含了几乎所有的内容,并禁用了调试功能),请使用defconfigmake目标:

BusyBox源代码树
BusyBox的源代码树组织得很好。这些工具都基于它们的用途进行了分类,并存储在单独的子目录中。例如,网络工具和守护进程(如httpdifconfig等)都在./networking目录中;标准的模块工具(包括insmodrmmodlsmod)都在./modutils目录中;编辑器(例如vi和流编辑器,如awksed)都在./editors目录中。makefile配置、编译和安装所使用的各个文档都在这个目录树的根目录中。

清单5.编译默认的BusyBox配置
___FCKpd___4nbsp;cdbusybox-1.1.1
___FCKpd___4nbsp;makedefconfig
___FCKpd___4nbsp;make
$

结果是一个相当大的BusyBox映像,不过这只是开始使用它的最简单的方法。我们可以直接调用这个新映像,这会产生一个简单的Help页面,里面包括当前配置的命令。要对这个映像进行测试,我们也可以对一个命令调用BusyBox来执行,如清单6所示。


清单6.展示BusyBox命令的执行和BusyBox中的ashshell
___FCKpd___5nbsp;./busyboxpwd
/usr/local/src/busybox-1.1.1
___FCKpd___5nbsp;./busyboxash
/usr/local/src/busybox-1.1.1___FCKpd___5nbsp;pwd
/usr/local/src/busybox-1.1.1
/usr/local/src/busybox-1.1.1___FCKpd___5nbsp;exit
$

在这个例子中,我们调用了pwd(打印工作目录)命令,使用BusyBox进入了ashshell,并在ash中调用了pwd





回页首


手工配置

如果您正在构建一个具有特殊需求的嵌入式设备,那就可以手工使用menuconfigmake目标来配置BusyBox的内容。如果您熟悉Linux内核的编译过程,就会注意到menuconfig与配置Linux内核的内容所使用的目标相同。实际上,它们都采用了相同的基于ncurses的应用程序。

使用手工配置,我们可以指定在最终的BusyBox映像中包含的命令。我们也可以对BusyBox环境进行配置,例如包括对NSA(美国国家安全代理)的安全增强Linux(SELinux),指定要使用的编译器(用来在嵌入式环境中进行交叉编译)以及BusyBox应该静态编译还是动态编译。图1给出了menuconfig的主界面。在这里我们应该可以看到可以为BusyBox配置的不同类型的应用程序(applet)。


图1.使用menuconfig配置BusyBox
使用 menuconfig 配置 BusyBox
多体系结构支持
可以简单地为BusyBox指定交叉编译器意味着我们可以为很多体系结构编译BusyBox。要为您的目标体系结构编译BusyBox,我们需要一个交叉编译器和一个已经为特定目标体系结构编译好的C库(uClibc或glibc)。

要手工配置BusyBox,请使用下面的命令:


清单7.手工配置BusyBox
___FCKpd___6nbsp;makemenuconfig
___FCKpd___6nbsp;make
$

这为我们提供了可以调用的BusyBox的二进制文件。下一个步骤是围绕BusyBox构建一个环境,包括将标准Linux命令重定向到BusyBox二进制文件的符号链接。我们可以使用下面的命令简单地完成这个过程:


清单8.构建BusyBox环境
___FCKpd___7nbsp;makeinstall
$

默认情况下,这会创建一个新的本地子目录_install,其中包含了基本的Linux环境。在这个根目录中,您会找到一个链接到BusyBox的linuxrc程序。这个linuxrc程序在构建安装盘或急救盘(允许提前进行模块化的引导)时非常有用。同样是在这个根目录中,还有一个包含操作系统二进制文件的/sbin子目录。还有一个包含用户二进制文件的/bin目录。在构建软盘发行版或嵌入式初始RAM磁盘时,我们可以将这个_install目录迁移到目标环境中。我们还可以使用make程序的PREFIX选项将安装目录重定向到其他位置。例如,下面的代码就使用/tmp/newtarget根目录来安装这些符号链接,而不是使用./_install目录:


清单9.将符号链接安装到另外一个目录中
___FCKpd___8nbsp;makePREFIX=/tmp/newtargetinstall
$

使用installmake目标创建的符号链接都来自于busybox.links文件。这个文件是在编译BusyBox时创建的,它包含了已经配置的命令清单。在执行install时,就会检查busybox.links文件确定要创建的符号链接。

到BusyBox的命令行链接也可以使用BusyBox在运行时动态创建。CONFIG_FEATURE_INSTALLER选项就可以启用这个特性,在运行时可以这样执行:


清单10.在运行时创建命令链接
___FCKpd___9nbsp;./busybox--install-s
$

-s选项强制创建这些符号链接(否则就创建硬链接)。这个选项要求系统中存在/proc文件系统。





回页首


BusyBox编译选项

BusyBox包括了几个编译选项,可以帮助为我们编译和调试正确的BusyBox。


表1.为BusyBox提供的几个make选项
make目标说明
help显示make选项的完整列表
defconfig启用默认的(通用)配置
allnoconfig禁用所有的应用程序(空配置)
allyesconfig启用所有的应用程序(完整配置)
allbareconfig启用所有的应用程序,但是不包括子特性
config基于文本的配置工具
menuconfigN-curses(基于菜单的)配置工具
all编译BusyBox二进制文件和文档(./docs)
busybox编译BusyBox二进制文件
clean清除源代码树
distclean彻底清除源代码树
sizes显示所启用的应用程序的文本/数据大小

在定义配置时,我们只需要输入make就可以真正编译BusyBox二进制文件。例如,要为所有的应用程序编译BusyBox,我们可以执行下面的命令:


清单11.编译BusyBox二进制程序
___FCKpd___10nbsp;makeallyesconfig
___FCKpd___10nbsp;make
$





回页首


压缩BusyBox

如果您非常关心对BusyBox映像的压缩,就需要记住两件事情:

  1. 永远不要编译为静态二进制文件(这会将所有需要的库都包含到映像文件中)。相反,如果我们是编译为一个共享映像,那么它会使用其他应用程序使用的库(例如/lib/libc.so.X)。
  2. 使用uClibc进行编译,这是一个对大小进行过优化的C库,它是为嵌入式系统开发的;而不要使用标准的glibc(GNUC库)来编译。




回页首


BusyBox命令中支持的选项

BusyBox中的命令并不支持所有可用选项,不过这些命令都包含了常用的选项。如果我们需要知道一个命令可以支持哪些选项,可以使用--help选项来调用这个命令,如清单12所示。


清单12.使用--help选项调用命令
___FCKpd___11nbsp;./busyboxwc--help
BusyBoxv1.1.1(2006.04.09-15:27+0000)multi-callbinary

Usage:wc[OPTION]...[FILE]...

Printline,word,andbytecountsforeachFILE,andatotallineif
morethanoneFILEisspecified.WithnoFILE,readstandardinput.

Options:
 -c printthebytecounts
 -l printthenewlinecounts
 -L printthelengthofthelongestline
 -w printthewordcounts

$

这些特定的数据只有在启用了CONFIG_FEATURE_VERBOSE_USAGE选项时才可以使用。如果没有这个选项,我们就无法获得这些详细数据,但是这样可以节省大约13KB的空间。





回页首


向BusyBox中添加新命令

向BusyBox添加一个新命令非常简单,这是因为它具有良好定义的体系结构。第一个步骤是为新命令的源代码选择一个位置。我们要根据命令的类型(网络,shell等)来选择位置,并与其他命令保持一致。这一点非常重要,因为这个新命令最终会在menuconfig的配置菜单中出现(在下面的例子中,是MiscellaneousUtilities菜单)。

对于这个例子来说,我将这个新命令称为newcmd,并将它放到了./miscutils目录中。这个新命令的源代码如清单13所示。


清单13.集成到BusyBox中的新命令的源代码
#include"busybox.h"

intnewcmd_main(intargc,char*argv[])
{
inti;

printf("newcmdcalled:/n");

for(i=0;i<argc;i++){

printf("arg[%d]=%s/n",i,argv[i]);

}

return0;
}

接下来,我们要将这个新命令的源代码添加到所选子目录中的Makefile.in中。在本例中,我更新了./miscutils/Makefile.in文件。请按照字母顺序来添加新命令,以便维持与现有命令的一致性:


清单14.将命令添加到Makefile.in中
MISCUTILS-$(CONFIG_MT)+=mt.o
MISCUTILS-$(CONFIG_NEWCMD)+=newcmd.o
MISCUTILS-$(CONFIG_RUNLEVEL)+=runlevel.o

接下来再次更新./miscutils目录中的配置文件,以便让新命令在配置过程中是可见的。这个文件名为Config.in,新命令是按照字母顺序添加的:


清单15.将命令添加到Config.in中
configCONFIG_NEWCMD
 bool"newcmd"
 defaultn
 help
 newcmdisanewtestcommand.

这个结构定义了一个新配置项(通过config关键字)以及一个配置选项(CONFIG_NEWCMD)。新命令可以启用,也可以禁用,因此我们对配置的菜单属性使用了bool(Boolean)值。这个命令默认是禁用的(n表示No),我们可以最后放上一个简短的Help描述。在源代码树的./scripts/config/Kconfig-language.txt文件中,我们可以看到配置语法的完整文法。

接下来需要更新./include/applets.h文件,使其包含这个新命令。将下面这行内容添加到这个文件中,记住要按照字母顺序。维护这个次序非常重要,否则我们的命令就会找不到。


清单16.将命令添加到applets.h中
USE_NEWCMD(APPLET(newcmd,newcmd_main,_BB_DIR_USER_BIN,_BB_SUID_NEVER))

这定义了命令名(newcmd),它在Busybox源代码中的函数名(newcmd_main),应该在哪里会为这个新命令创建链接(在这种情况中,它在/usr/bin目录中),最后这个命令是否有权设置用户id(在本例中是no)。

倒数第二个步骤是向./include/usage.h文件中添加详细的帮助信息。正如您可以从这个文件的例子中看到的一样,使用信息可能非常详细。在本例中,我只添加了一点信息,这样就可以编译这个新命令了:


清单17.向usage.h添加帮助信息
#definenewcmd_trivial_usage "None"
#definenewcmd_full_usage "None"

最后一个步骤是启用新命令(通过makemenuconfig,然后在MiscellaneousUtilities菜单中启用这个选项)然后使用make来编译BusyBox。

使用新的BusyBox,我们可以对这个新命令进行测试,如清单18所示。


清单18.测试新命令
___FCKpd___17nbsp;./busyboxnewcmdarg1
newcmdcalled:
arg[0]=newcmd
arg[1]=arg1
___FCKpd___17nbsp;./busyboxnewcmd--help
BusyBoxv1.1.1(2006.04.12-13:47+0000)multi-callbinary

Usage:newcmdNone

None

就是这样!BusyBox开发人员开发了一个优秀但非常容易扩展的工具。





回页首


结束语

BusyBox是为构建内存有限的嵌入式系统和基于软盘系统的一个优秀工具。BusyBox通过将很多必需的工具放入一个可执行程序,并让它们可以共享代码中相同的部分,从而对它们的大小进行了很大程度的缩减,BusyBox对于嵌入式系统来说是一个非常有用的工具,因此值得我们花一些时间进行探索。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值