BusyBox 是很多標準 Linux® 工具的一個單個可執行實現。BusyBox 包含了一些簡單的工具,例如 cat 和 echo,還包含了一些更大、更復雜的工具,例如 grep、find、mount 以及 telnet(不過它的選項比傳統的版本要少);有些人將 BusyBox 稱爲 Linux 工具裏的瑞士軍刀。本文將探索 BusyBox 的目標,它是如何工作的,以及爲什麼它對於內存有限的環境來說是如此重要。
BusyBox 最初是由 Bruce Perens 在 1996 年爲 Debian GNU/Linux 安裝盤編寫的。其目標是在一張軟盤上創建一個可引導的 GNU/Linux 系統,這可以用作安裝盤和急救盤。一張軟盤可以保存大約 1.4-1.7MB 的內容,因此這裏沒有多少空間留給 Linux 內核以及相關的用戶應用程序使用。
BusyBox 揭露了這樣一個事實:很多標準 Linux 工具都可以共享很多共同的元素。例如,很多基於文件的工具(比如grep
和find
)都需要在目錄中搜索文件的代碼。當這些工具被合併到一個可執行程序中時,它們就可以共享這些相同的元素,這樣可以產生更小的可執行程序。實際上, BusyBox 可以將大約 3.5MB 的工具包裝成大約 200KB 大小。這就爲可引導的磁盤和使用 Linux 的嵌入式設備提供了更多功能。我們可以對 2.4 和 2.6 版本的 Linux 內核使用 BusyBox。
爲了讓一個可執行程序看起來就像是很多可執行程序一樣,BusyBox 爲傳遞給 C 的 main 函數的參數開發了一個很少使用的特性。回想一下 C 語言的 main 函數的定義如下:
int main( int argc, char *argv[] )
在這個定義中,argc
是傳遞進來的參數的個數(參數數量),而argv
是一個字符串數組,代表從命令行傳遞進來的參數(參數向量)。argv
的索引 0 是從命令行調用的程序名。
清單 2 給出的這個簡單 C 程序展示了 BusyBox 的調用。它只簡單地打印argv
向量的內容。
清單 2. BusyBox 使用 argv[0] 來確定調用哪個應用程序
// 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;
}
調用這個程序會顯示所調用的第一個參數是該程序的名字。我們可以對這個可執行程序重新進行命名,此時再調用就會得到該程序的新名字。另外,我們可以創建一個到可執行程序的符號鏈接,在執行這個符號鏈接時,就可以看到這個符號鏈接的名字。
清單 3. 在使用新命令更新 BusyBox 之後的命令測試
$ 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 使用了符號鏈接以便使一個可執行程序看起來像很多程序一樣。對於 BusyBox 中包含的每個工具來說,都會這樣創建一個符號鏈接,這樣就可以使用這些符號鏈接來調用 BusyBox 了。BusyBox 然後可以通過argv[0]
來調用內部工具。
我們可以從 BusyBox 的 Web 站點上下載最新版本的 BusyBox(請參看參考資料一節的內容)。與大部分開放源碼程序一樣,它是以一個壓縮的 tarball 形式發佈的,我們可以使用清單 4 給出的命令將其轉換成源代碼樹。(如果我們下載的版本不是 1.1.1,那就請在這個命令中使用適當的版本號以及特定於這個版本號的命令。)
$ tar xvfz busybox-1.1.1.tar.gz $
結果會生成一個目錄,名爲 busybox-1.1.1,其中包含了 BusyBox 的源代碼。要編譯默認的配置(其中包含了幾乎所有的內容,並禁用了調試功能),請使用defconfig
make 目標:
$ cd busybox-1.1.1$ make defconfig$ make $
結果是一個相當大的 BusyBox 映像,不過這只是開始使用它的最簡單的方法。我們可以直接調用這個新映像,這會產生一個簡單的 Help 頁面,裏面包括當前配置的命令。要對這個映像進行測試,我們也可以對一個命令調用 BusyBox 來執行,如清單 6 所示。
清單 6. 展示 BusyBox 命令的執行和 BusyBox 中的 ash shell
$ ./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 $
在這個例子中,我們調用了pwd
(打印工作目錄)命令,使用 BusyBox 進入了ash
shell,並在ash
中調用了pwd
。
如果您正在構建一個具有特殊需求的嵌入式設備,那就可以手工使用menuconfig
make 目標來配置 BusyBox 的內容。如果您熟悉 Linux 內核的編譯過程,就會注意到menuconfig
與配置 Linux 內核的內容所使用的目標相同。實際上,它們都採用了相同的基於 ncurses 的應用程序。
使用手工配置,我們可以指定在最終的 BusyBox 映像中包含的命令。我們也可以對 BusyBox 環境進行配置,例如包括對 NSA(美國國家安全代理)的安全增強 Linux(SELinux),指定要使用的編譯器(用來在嵌入式環境中進行交叉編譯)以及 BusyBox 應該靜態編譯還是動態編譯。圖 1 給出了menuconfig
的主界面。在這裏我們應該可以看到可以爲 BusyBox 配置的不同類型的應用程序(applet)。
圖 1. 使用 menuconfig 配置 BusyBox
650) this.width=650;" height="556" alt="使用 menuconfig 配置 BusyBox" src="http://linux.chinaunix.net/mirror/www-128.ibm.com/developerworks/cn/linux/l-busybox/figure1.jpg" width="572" title="點擊圖片可在新窗口打開" style="word-break:break-all;max-width:400px;cursor:pointer;" />
要手工配置 BusyBox,請使用下面的命令:
$ make menuconfig $ make $
這爲我們提供了可以調用的 BusyBox 的二進制文件。下一個步驟是圍繞 BusyBox 構建一個環境,包括將標準 Linux 命令重定向到 BusyBox 二進制文件的符號鏈接。我們可以使用下面的命令簡單地完成這個過程:
$ make install $
默認情況下,這會創建一個新的本地子目錄 _install,其中包含了基本的 Linux 環境。在這個根目錄中,您會找到一個鏈接到 BusyBox 的linuxrc
程序。這個linuxrc
程序在構建安裝盤或急救盤(允許提前進行模塊化的引導)時非常有用。同樣是在這個根目錄中,還有一個包含操作系統二進制文件的 /sbin 子目錄。還有一個包含用戶二進制文件的 /bin 目錄。在構建軟盤發行版或嵌入式初始 RAM 磁盤時,我們可以將這個 _install 目錄遷移到目標環境中。我們還可以使用 make 程序的PREFIX
選項將安裝目錄重定向到其他位置。例如,下面的代碼就使用 /tmp/newtarget 根目錄來安裝這些符號鏈接,而不是使用 ./_install 目錄:
$ make PREFIX=/tmp/newtarget install $
使用install
make 目標創建的符號鏈接都來自於 busybox.links 文件。這個文件是在編譯 BusyBox 時創建的,它包含了已經配置的命令清單。在執行install
時,就會檢查 busybox.links 文件確定要創建的符號鏈接。
到 BusyBox 的命令行鏈接也可以使用 BusyBox 在運行時動態創建。CONFIG_FEATURE_INSTALLER
選項就可以啓用這個特性,在運行時可以這樣執行:
$ ./busybox --install -s $
-s
選項強制創建這些符號鏈接(否則就創建硬鏈接)。這個選項要求系統中存在 /proc 文件系統。
BusyBox 包括了幾個編譯選項,可以幫助爲我們編譯和調試正確的 BusyBox。
make 目標 | 說明 |
---|---|
help | 顯示 make 選項的完整列表 |
defconfig | 啓用默認的(通用)配置 |
allnoconfig | 禁用所有的應用程序(空配置) |
allyesconfig | 啓用所有的應用程序(完整配置) |
allbareconfig | 啓用所有的應用程序,但是不包括子特性 |
config | 基於文本的配置工具 |
menuconfig | N-curses(基於菜單的)配置工具 |
all | 編譯 BusyBox 二進制文件和文檔(./docs) |
busybox | 編譯 BusyBox 二進制文件 |
clean | 清除源代碼樹 |
distclean | 徹底清除源代碼樹 |
sizes | 顯示所啓用的應用程序的文本/數據大小 |
在定義配置時,我們只需要輸入make
就可以真正編譯 BusyBox 二進制文件。例如,要爲所有的應用程序編譯 BusyBox,我們可以執行下面的命令:
$ make allyesconfig $ make $
如果您非常關心對 BusyBox 映像的壓縮,就需要記住兩件事情:
永遠不要編譯爲靜態二進制文件(這會將所有需要的庫都包含到映像文件中)。相反,如果我們是編譯爲一個共享映像,那麼它會使用其他應用程序使用的庫(例如
/lib/libc.so.X
)。使用 uClibc 進行編譯,這是一個對大小進行過優化的 C 庫,它是爲嵌入式系統開發的;而不要使用標準的 glibc (GNU C 庫)來編譯。
BusyBox 中的命令並不支持所有可用選項,不過這些命令都包含了常用的選項。如果我們需要知道一個命令可以支持哪些選項,可以使用--help
選項來調用這個命令,如清單 12 所示。
$ ./busybox wc --help BusyBox v1.1.1 (2006.04.09-15:27+0000) multi-call binaryUsage: wc [OPTION]... [FILE]...Print line, word, and byte counts for each FILE, and a total line ifmore than one FILE is specified. With no FILE, read standard input.Options:-cprint the byte counts-lprint the newline counts-Lprint the length of the longest line-wprint the word counts$
這些特定的數據只有在啓用了CONFIG_FEATURE_VERBOSE_USAGE
選項時纔可以使用。如果沒有這個選項,我們就無法獲得這些詳細數據,但是這樣可以節省大約 13 KB 的空間。
向 BusyBox 添加一個新命令非常簡單,這是因爲它具有良好定義的體系結構。第一個步驟是爲新命令的源代碼選擇一個位置。我們要根據命令的類型(網絡,shell 等)來選擇位置,並與其他命令保持一致。這一點非常重要,因爲這個新命令最終會在 menuconfig 的配置菜單中出現(在下面的例子中,是 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
文件。請按照字母順序來添加新命令,以便維持與現有命令的一致性:
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.
這個結構定義了一個新配置項(通過config
關鍵字)以及一個配置選項(CONFIG_NEWCMD
)。新命令可以啓用,也可以禁用,因此我們對配置的菜單屬性使用了bool
(Boolean)值。這個命令默認是禁用的(n
表示 No),我們可以最後放上一個簡短的 Help 描述。在源代碼樹的 ./scripts/config/Kconfig-language.txt 文件中,我們可以看到配置語法的完整文法。
接下來需要更新 ./include/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 文件中添加詳細的幫助信息。正如您可以從這個文件的例子中看到的一樣,使用信息可能非常詳細。在本例中,我只添加了一點信息,這樣就可以編譯這個新命令了:
#define newcmd_trivial_usage"None"#define newcmd_full_usage"None"
最後一個步驟是啓用新命令(通過make menuconfig
,然後在 Miscellaneous Utilities 菜單中啓用這個選項)然後使用make
來編譯 BusyBox。
使用新的 BusyBox,我們可以對這個新命令進行測試,如清單 18 所示。
$ ./busybox newcmd arg1newcmd 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 開發人員開發了一個優秀但非常容易擴展的工具。
BusyBox 是爲構建內存有限的嵌入式系統和基於軟盤系統的一個優秀工具。BusyBox 通過將很多必需的工具放入一個可執行程序,並讓它們可以共享代碼中相同的部分,從而對它們的大小進行了很大程度的縮減,BusyBox 對於嵌入式系統來說是一個非常有用的工具,因此值得我們花一些時間進行探索。
原文鏈接:http://www-128.ibm.com/developerworks/cn/linux/l-busybox/index.html