歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> Linux文化

BusyBox 超級簡化嵌入式 Linux 系統


BusyBox 是很多標准 Linux® 工具的一個單個可執行實現。BusyBox 包含了一些簡單的工具,例如 cat 和 echo,還包含了一些更大、更復雜的工具,例如 grep、find、mount 以及 telnet(不過它的選項比傳統的版本要少);有些人將 BusyBox 稱為 Linux 工具裡的瑞士軍刀。本文將探索 BusyBox 的目標,它是如何工作的,以及為什麼它對於內存有限的環境來說是如此重要。

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 是如何工作的?

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

清單 1. C 的 main 函數

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

在這個定義中,argc 是傳遞進來的參數的個數(參數數量),而 argv 是一個字符串數組,代表從命令行傳遞進來的參數(參數向量)。argv 的索引 0 是從命令行調用的程序名。

清單 2 給出的這個簡單 C 程序展示了 BusyBox 的調用。它只簡單地打印 argv 向量的內容。

清單 2. BusyBox 使用 argv[0] 來確定調用哪個應用程序

// test.c #include 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

我們可以從 BusyBox 的 Web 站點上下載最新版本的 BusyBox(請參看 參考資料 一節的內容)。與大部分開放源碼程序一樣,它是以一個壓縮的 tarball 形式發布的,我們可以使用清單 4 給出的命令將其轉換成源代碼樹。(如果我們下載的版本不是 1.1.1,那就請在這個命令中使用適當的版本號以及特定於這個版本號的命令。)

清單 4. 展開 BusyBox

$ tar xvfz busybox-1.1.1.tar.gz $

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

清單 5. 編譯默認的 BusyBox 配置

$ 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

多體系結構支持

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

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

清單 7. 手工配置 BusyBox

$ make menuconfig $ make $

這為我們提供了可以調用的 BusyBox 的二進制文件。下一個步驟是圍繞 BusyBox 構建一個環境,包括將標准 Linux 命令重定向到 BusyBox 二進制文件的符號鏈接。我們可以使用下面的命令簡單地完成這個過程:

清單 8. 構建 BusyBox 環境

$ make install $

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

清單 9. 將符號鏈接安裝到另外一個目錄中

$ make PREFIX=/tmp/newtarget install $

使用 install make 目標創建的符號鏈接都來自於 busybox.links 文件。這個文件是在編譯 BusyBox 時創建的,它包含了已經配置的命令清單。在執行 install 時,就會檢查 busybox.links 文件確定要創建的符號鏈接。

到 BusyBox 的命令行鏈接也可以使用 BusyBox 在運行時動態創建。CONFIG_FEATURE_INSTALLER 選項就可以啟用這個特性,在運行時可以這樣執行:

清單 10. 在運行時創建命令鏈接

$ ./busybox --install -s $

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

BusyBox 編譯選項

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

表 1. 為 BusyBox 提供的幾個 make 選項

make目標 說明

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

在定義配置時,我們只需要輸入 make 就可以真正編譯 BusyBox 二進制文件。例如,要為所有的應用程序編譯 BusyBox,我們可以執行下面的命令:

清單 11. 編譯 BusyBox 二進制程序

$ make allyesconfig $ make $

壓縮 BusyBox

如果您非常關心對 BusyBox 映像的壓縮,就需要記住兩件事情:

1. 永遠不要編譯為靜態二進制文件(這會將所有需要的庫都包含到映像文件中)。相反,如果我們是編譯為一個共享映像,那麼它會使用其他應用程序使用的庫(例如 /lib/libc.so.X)。

2. 使用 uClibc 進行編譯,這是一個對大小進行過優化的 C 庫,它是為嵌入式系統開發的;而不要使用標准的 glibc (GNU C 庫)來編譯。

BusyBox 命令中支持的選項

BusyBox 中的命令並不支持所有可用選項,不過這些命令都包含了常用的選項。如果我們需要知道一個命令可以支持哪些選項,可以使用 --help 選項來調用這個命令,如清單 12 所示。

清單 12. 使用 --help 選項調用命令

$ ./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 $

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

向 BusyBox 中添加新命令

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

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

清單 13. 集成到 BusyBox 中的新命令的源代碼

#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 文件。請按照字母順序來添加新命令,以便維持與現有命令的一致性:

清單 14. 將命令添加到 Makefile.in 中

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

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

清單 15. 將命令添加到 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 文件,使其包含這個新命令。將下面這行內容添加到這個文件中,記住要按照字母順序。維護這個次序非常重要,否則我們的命令就會找不到。

清單 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 添加幫助信息

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

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

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

清單 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 開發人員開發了一個優秀但非常容易擴展的工具。

結束語

BusyBox 是為構建內存有限的嵌入式系統和基於軟盤系統的一個優秀工具。BusyBox 通過將很多必需的工具放入一個可執行程序,並讓它們可以共享代碼中相同的部分,從而對它們的大小進行了很大程度的縮減,BusyBox 對於嵌入式系統來說是一個非常有用的工具,因此值得我們花一些時間進行探索。

Copyright © Linux教程網 All Rights Reserved