標(biāo)題: BusyBox簡化嵌入式Linux系統(tǒng) [打印本頁]

作者: 51黑tt    時間: 2016-3-6 15:14
標(biāo)題: BusyBox簡化嵌入式Linux系統(tǒng)

BusyBox 是很多標(biāo)準(zhǔn) Linux® 工具的一個單個可執(zhí)行實現(xiàn)。BusyBox 包含了一些簡單的工具,例如cat 和 echo,還包含了一些更大、更復(fù)雜的工具,例如 grep、find、mount 以及 telnet(不過它的選項比傳統(tǒng)的版本要少);有些人將 BusyBox 稱為 Linux 工具里的瑞士軍刀。本文將探索 BusyBox 的目標(biāo),它是如何工作的,以及為什么它對于內(nèi)存有限的環(huán)境來說是如此重要。

BusyBox 最初是由 Bruce Perens 在 1996 年為 Debian GNU/Linux 安裝盤編寫的。其目標(biāo)是在一張軟盤上創(chuàng)建一個可引導(dǎo)的 GNU/Linux 系統(tǒng),這可以用作安裝盤和急救盤。一張軟盤可以保存大約 1.4-1.7MB 的內(nèi)容,因此這里沒有多少空間留給 Linux 內(nèi)核以及相關(guān)的用戶應(yīng)用程序使用。

BusyBox 揭露了這樣一個事實:很多標(biāo)準(zhǔn) Linux 工具都可以共享很多共同的元素。例如,很多基于文件的工具(比如 grep 和 find)都需要在目錄中搜索文件的代碼。當(dāng)這些工具被合并到一個可執(zhí)行程序中時,它們就可以共享這些相同的元素,這樣可以產(chǎn)生更小的可執(zhí)行程序。實際上,BusyBox 可以將大約 3.5MB 的工具包裝成大約 200KB 大小。這就為可引導(dǎo)的磁盤和使用 Linux 的嵌入式設(shè)備提供了更多功能。我們可以對 2.4 和 2.6 版本的 Linux 內(nèi)核使用 BusyBox。

BusyBox 是按照 GNU General Public License(GPL)許可證發(fā)行的。這意味著如果我們在一個項目中使用 BusyBox,就必須遵守這個許可證。我們可以在 BusyBox Web 站點上看到這個許可證的內(nèi)容。BusyBox 團(tuán)隊似乎正忙于監(jiān)視違反這個許可證的情況。實際上,他們維護(hù)了一個 “Hall of Shame” 頁面來說明違反者的情況。 


為了讓一個可執(zhí)行程序看起來就像是很多可執(zhí)行程序一樣,BusyBox 為傳遞給 C 的 main 函數(shù)的參數(shù)開發(fā)了一個很少使用的特性。回想一下 C 語言的 main 函數(shù)的定義如下:

int main( int argc, char *argv[] )
 

盡管 BusyBox 的目標(biāo) 是提供一個相當(dāng)完整的 POSIX(可移植操作系統(tǒng)接口)環(huán)境,這是一個期望,而不是一種需求。這些工具雖然并不完整,但是它們提供了我們期望的主要功能。 


在這個定義中,argc 是傳遞進(jìn)來的參數(shù)的個數(shù)(參數(shù)數(shù)量),而 argv 是一個字符串?dāng)?shù)組,代表從命令行傳遞進(jìn)來的參數(shù)(參數(shù)向量)。argv 的索引 0 是從命令行調(diào)用的程序名。

清單 2 給出的這個簡單 C 程序展示了 BusyBox 的調(diào)用。它只簡單地打印 argv 向量的內(nèi)容。
// test.c
#include <stdio.h>

int main( int argc, char *argv[] )
{
  int i;

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

  return 0;
}
 

調(diào)用這個程序會顯示所調(diào)用的第一個參數(shù)是該程序的名字。我們可以對這個可執(zhí)行程序重新進(jìn)行命名,此時再調(diào)用就會得到該程序的新名字。另外,我們可以創(chuàng)建一個到可執(zhí)行程序的符號鏈接,在執(zhí)行這個符號鏈接時,就可以看到這個符號鏈接的名字。

$ gcc -Wall -o test test.c
$ ./test arg1 arg2
argv[0] = ./test
argv[1] = arg1
argv[2] = arg2

$ mv test newtest
$ ./newtest arg1
argv[0] = ./newtest
argv[1] = arg1

$ ln -s newtest linktest
$ ./linktest arg
argv[0] = ./linktest
argv[1] = arg
 


BusyBox 使用了符號鏈接以便使一個可執(zhí)行程序看起來像很多程序一樣。對于 BusyBox 中包含的每個工具來說,都會這樣創(chuàng)建一個符號鏈接,這樣就可以使用這些符號鏈接來調(diào)用 BusyBox 了。BusyBox 然后可以通過 argv[0] 來調(diào)用內(nèi)部工具。

我們可以從 BusyBox 的 Web 站點上下載最新版本的 BusyBox(請參看 參考資料 一節(jié)的內(nèi)容)。與大部分開放源碼程序一樣,它是以一個壓縮的 tarball 形式發(fā)布的,我們可以使用清單 4 給出的命令將其轉(zhuǎn)換成源代碼樹。(如果我們下載的版本不是 1.1.1,那就請在這個命令中使用適當(dāng)?shù)陌姹咎栆约疤囟ㄓ谶@個版本號的命令。)
$ tar xvfz busybox-1.1.1.tar.gz
$
 

 

結(jié)果會生成一個目錄,名為 busybox-1.1.1,其中包含了 BusyBox 的源代碼。要編譯默認(rèn)的配置(其中包含了幾乎所有的內(nèi)容,并禁用了調(diào)試功能),請使用 defconfig make 目標(biāo):

$ cd busybox-1.1.1
$ make defconfig
$ make
$
 

BusyBox 的源代碼樹組織得很好。這些工具都基于它們的用途進(jìn)行了分類,并存儲在單獨的子目錄中。例如,網(wǎng)絡(luò)工具和守護(hù)進(jìn)程(如 httpd、ifconfig 等)都在 ./networking 目錄中;標(biāo)準(zhǔn)的模塊工具(包括 insmod、rmmod 和 lsmod)都在 ./modutils 目錄中;編輯器(例如 vi 和流編輯器,如 awk 和 sed)都在 ./editors 目錄中。makefile 配置、編譯和安裝所使用的各個文檔都在這個目錄樹的根目錄中。 


結(jié)果是一個相當(dāng)大的 BusyBox 映像,不過這只是開始使用它的最簡單的方法。我們可以直接調(diào)用這個新映像,這會產(chǎn)生一個簡單的 Help 頁面,里面包括當(dāng)前配置的命令。要對這個映像進(jìn)行測試,我們也可以對一個命令調(diào)用 BusyBox 來執(zhí)行,如清單 6 所示。


$ ./busybox pwd
/usr/local/src/busybox-1.1.1
$ ./busybox ash
/usr/local/src/busybox-1.1.1 $ pwd
/usr/local/src/busybox-1.1.1
/usr/local/src/busybox-1.1.1 $ exit
$
 


在這個例子中,我們調(diào)用了 pwd(打印工作目錄)命令,使用 BusyBox 進(jìn)入了 ash shell,并在 ash 中調(diào)用了 pwd。

如果您正在構(gòu)建一個具有特殊需求的嵌入式設(shè)備,那就可以手工使用 menuconfig make 目標(biāo)來配置 BusyBox 的內(nèi)容。如果您熟悉 Linux 內(nèi)核的編譯過程,就會注意到 menuconfig 與配置 Linux 內(nèi)核的內(nèi)容所使用的目標(biāo)相同。實際上,它們都采用了相同的基于 ncurses 的應(yīng)用程序。

使用手工配置,我們可以指定在最終的 BusyBox 映像中包含的命令。我們也可以對 BusyBox 環(huán)境進(jìn)行配置,例如包括對 NSA(美國國家安全代理)的安全增強(qiáng) Linux(SELinux),指定要使用的編譯器(用來在嵌入式環(huán)境中進(jìn)行交叉編譯)以及 BusyBox 應(yīng)該靜態(tài)編譯還是動態(tài)編譯。圖 1 給出了 menuconfig 的主界面。在這里我們應(yīng)該可以看到可以為 BusyBox 配置的不同類型的應(yīng)用程序(applet)。



 

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

$ make menuconfig
$ make
$
 

可以簡單地為 BusyBox 指定交叉編譯器意味著我們可以為很多體系結(jié)構(gòu)編譯 BusyBox。要為您的目標(biāo)體系結(jié)構(gòu)編譯 BusyBox,我們需要一個交叉編譯器和一個已經(jīng)為特定目標(biāo)體系結(jié)構(gòu)編譯好的 C 庫(uClibc 或 glibc)。 


這為我們提供了可以調(diào)用的 BusyBox 的二進(jìn)制文件。下一個步驟是圍繞 BusyBox 構(gòu)建一個環(huán)境,包括將標(biāo)準(zhǔn) Linux 命令重定向到 BusyBox 二進(jìn)制文件的符號鏈接。我們可以使用下面的命令簡單地完成這個過程:
$ make install
$
 

 

默認(rèn)情況下,這會創(chuàng)建一個新的本地子目錄 _install,其中包含了基本的 Linux 環(huán)境。在這個根目錄中,您會找到一個鏈接到 BusyBox 的 linuxrc 程序。這個 linuxrc 程序在構(gòu)建安裝盤或急救盤(允許提前進(jìn)行模塊化的引導(dǎo))時非常有用。同樣是在這個根目錄中,還有一個包含操作系統(tǒng)二進(jìn)制文件的 /sbin 子目錄。還有一個包含用戶二進(jìn)制文件的 /bin 目錄。在構(gòu)建軟盤發(fā)行版或嵌入式初始 RAM 磁盤時,我們可以將這個 _install 目錄遷移到目標(biāo)環(huán)境中。我們還可以使用 make 程序的 PREFIX 選項將安裝目錄重定向到其他位置。例如,下面的代碼就使用 /tmp/newtarget 根目錄來安裝這些符號鏈接,而不是使用 ./_install 目錄:


$ make PREFIX=/tmp/newtarget install
$
 


使用 install make 目標(biāo)創(chuàng)建的符號鏈接都來自于 busybox.links 文件。這個文件是在編譯 BusyBox 時創(chuàng)建的,它包含了已經(jīng)配置的命令清單。在執(zhí)行 install 時,就會檢查 busybox.links 文件確定要創(chuàng)建的符號鏈接。

到 BusyBox 的命令行鏈接也可以使用 BusyBox 在運行時動態(tài)創(chuàng)建。CONFIG_FEATURE_INSTALLER 選項就可以啟用這個特性,在運行時可以這樣執(zhí)行:


$ ./busybox --install -s
$
 


-s 選項強(qiáng)制創(chuàng)建這些符號鏈接(否則就創(chuàng)建硬鏈接)。這個選項要求系統(tǒng)中存在 /proc 文件系統(tǒng)。

 

BusyBox 包括了幾個編譯選項,可以幫助為我們編譯和調(diào)試正確的 BusyBox。

make 目標(biāo) 說明
help 顯示 make 選項的完整列表
defconfig 啟用默認(rèn)的(通用)配置
allnoconfig 禁用所有的應(yīng)用程序(空配置)
allyesconfig 啟用所有的應(yīng)用程序(完整配置)
allbareconfig 啟用所有的應(yīng)用程序,但是不包括子特性
config 基于文本的配置工具
menuconfig N-curses(基于菜單的)配置工具
all 編譯 BusyBox 二進(jìn)制文件和文檔(./docs)
busybox 編譯 BusyBox 二進(jìn)制文件
clean 清除源代碼樹
distclean 徹底清除源代碼樹
sizes 顯示所啟用的應(yīng)用程序的文本/數(shù)據(jù)大小

在定義配置時,我們只需要輸入 make 就可以真正編譯 BusyBox 二進(jìn)制文件。例如,要為所有的應(yīng)用程序編譯 BusyBox,我們可以執(zhí)行下面的命令:
$ make allyesconfig
$ make
$
 
如果您非常關(guān)心對 BusyBox 映像的壓縮,就需要記住兩件事情:

永遠(yuǎn)不要編譯為靜態(tài)二進(jìn)制文件(這會將所有需要的庫都包含到映像文件中)。相反,如果我們是編譯為一個共享映像,那么它會使用其他應(yīng)用程序使用的庫(例如 /lib/libc.so.X)。
使用 uClibc 進(jìn)行編譯,這是一個對大小進(jìn)行過優(yōu)化的 C 庫,它是為嵌入式系統(tǒng)開發(fā)的;而不要使用標(biāo)準(zhǔn)的 glibc (GNU C 庫)來編譯。
BusyBox 中的命令并不支持所有可用選項,不過這些命令都包含了常用的選項。如果我們需要知道一個命令可以支持哪些選項,可以使用 --help 選項來調(diào)用這個命令,如清單 12 所示。

$ ./busybox wc --help
BusyBox v1.1.1 (2006.04.09-15:27+0000) multi-call binary

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

Print line, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, read standard input.

Options:
 -c print the byte counts
 -l print the newline counts
 -L print the length of the longest line
 -w print the word counts

$
 


這些特定的數(shù)據(jù)只有在啟用了 CONFIG_FEATURE_VERBOSE_USAGE 選項時才可以使用。如果沒有這個選項,我們就無法獲得這些詳細(xì)數(shù)據(jù),但是這樣可以節(jié)省大約 13 KB 的空間。

向 BusyBox 添加一個新命令非常簡單,這是因為它具有良好定義的體系結(jié)構(gòu)。第一個步驟是為新命令的源代碼選擇一個位置。我們要根據(jù)命令的類型(網(wǎng)絡(luò),shell 等)來選擇位置,并與其他命令保持一致。這一點非常重要,因為這個新命令最終會在 menuconfig 的配置菜單中出現(xiàn)(在下面的例子中,是 Miscellaneous Utilities 菜單)。

對于這個例子來說,我將這個新命令稱為 newcmd,并將它放到了 ./miscutils 目錄中。這個新命令的源代碼如清單 13 所示。


#include "busybox.h"

int newcmd_main( int argc, char *argv[] )
{
  int i;

  printf("newcmd called:\n");

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

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

  }

  return 0;
}
 


接下來,我們要將這個新命令的源代碼添加到所選子目錄中的 Makefile.in 中。在本例中,我更新了 ./miscutils/Makefile.in 文件。請按照字母順序來添加新命令,以便維持與現(xiàn)有命令的一致性:

MISCUTILS-$(CONFIG_MT)          += mt.o
MISCUTILS-$(CONFIG_NEWCMD)   += newcmd.o
MISCUTILS-$(CONFIG_RUNLEVEL)    += runlevel.o
 


接下來再次更新 ./miscutils 目錄中的配置文件,以便讓新命令在配置過程中是可見的。這個文件名為 Config.in,新命令是按照字母順序添加的:

config CONFIG_NEWCMD
 bool "newcmd"
 default n
 help
   newcmd is a new test command.
 


這個結(jié)構(gòu)定義了一個新配置項(通過 config 關(guān)鍵字)以及一個配置選項(CONFIG_NEWCMD)。新命令可以啟用,也可以禁用,因此我們對配置的菜單屬性使用了 bool (Boolean)值。這個命令默認(rèn)是禁用的(n 表示 No),我們可以最后放上一個簡短的 Help 描述。在源代碼樹的 ./scripts/config/Kconfig-language.txt 文件中,我們可以看到配置語法的完整文法。

接下來需要更新 ./include/applets.h 文件,使其包含這個新命令。將下面這行內(nèi)容添加到這個文件中,記住要按照字母順序。維護(hù)這個次序非常重要,否則我們的命令就會找不到。


USE_NEWCMD(APPLET(newcmd, newcmd_main, _BB_DIR_USER_BIN, _BB_SUID_NEVER))
 


這定義了命令名(newcmd),它在 Busybox 源代碼中的函數(shù)名(newcmd_main),應(yīng)該在哪里會為這個新命令創(chuàng)建鏈接(在這種情況中,它在 /usr/bin 目錄中),最后這個命令是否有權(quán)設(shè)置用戶 id(在本例中是 no)。

倒數(shù)第二個步驟是向 ./include/usage.h 文件中添加詳細(xì)的幫助信息。正如您可以從這個文件的例子中看到的一樣,使用信息可能非常詳細(xì)。在本例中,我只添加了一點信息,這樣就可以編譯這個新命令了:


#define newcmd_trivial_usage "None"
#define newcmd_full_usage "None"
 


最后一個步驟是啟用新命令(通過 make menuconfig,然后在 Miscellaneous Utilities 菜單中啟用這個選項)然后使用 make 來編譯 BusyBox。

使用新的 BusyBox,我們可以對這個新命令進(jìn)行測試,如清單 18 所示。


$ ./busybox newcmd arg1
newcmd called:
arg[0] = newcmd
arg[1] = arg1
$ ./busybox newcmd --help
BusyBox v1.1.1 (2006.04.12-13:47+0000) multi-call binary

Usage: newcmd None

None
 


就是這樣!BusyBox 開發(fā)人員開發(fā)了一個優(yōu)秀但非常容易擴(kuò)展的工具。

BusyBox 是為構(gòu)建內(nèi)存有限的嵌入式系統(tǒng)和基于軟盤系統(tǒng)的一個優(yōu)秀工具。BusyBox 通過將很多必需的工具放入一個可執(zhí)行程序,并讓它們可以共享代碼中相同的部分,從而對它們的大小進(jìn)行了很大程度的縮減,BusyBox 對于嵌入式系統(tǒng)來說是一個非常有用的工具,因此值得我們花一些時間進(jìn)行探索。






歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1