1、BusyBox簡介
BusyBox 是很多標准 Linux 工具的一個單個可執行實現。BusyBox 包含了一些簡單的工具,例如 cat 和 echo,還包含了一些更大、更復雜的工具,例如 grep、find、mount 以及 telnet;有些人將 BusyBox 稱為 Linux 工具里的“瑞士軍刀”。
BusyBox 揭露了這樣一個事實:很多標准 Linux 工具都可以共享很多共同的元素。例如,很多基於文件的工具(比如 grep 和 find)都需要在目錄中搜索文件的代碼。當這些工具被合並到一個可執行程序中時,它們就可以共享這些相同的元素,這樣可以產生更小的可執行程序。實際上,BusyBox 可以將大約 3.5MB 的工具包裝成大約 200KB 大小。這就為可引導的磁盤和使用 Linux 的嵌入式設備提供了更多功能。我們可以對 2.4 和 2.6 版本的 Linux 內核使用 BusyBox。
我們平時用的那些Linux命令就好比是分立式的電子元件,而BusyBox就好比是一個集成電路,把常用的工具和命令集成壓縮在一個可執行文件里,功能基本不變,而大小卻小很多倍,在嵌入式linux應用中,BusyBox有非常廣的應用。
2、BusyBox的用法
可以這樣用BusyBox
#busybox ls
他的功能就相當運行ls命令
最常用的用法是建立指向BusyBox的鏈接,不同的鏈接名完成不同的功能.
#ln -s busybox ls
#ln -s busybox rm
#ln -s busybox mkdir
然后分別運行這三個鏈接:
#./ls
#./rm
#./mkdir
就可以分別完成了ls、rm和mkdir命令的功能.雖然他們都指向同一個可執行程序BusyBox,但是只要鏈接名不同,完成的功能就不同, BusyBox就是這么的神奇。
3、配置編譯BusyBox
? 下載
首先需要下載BusyBox源代碼。可從http://busybox.net上直接下載最新版BusyBox。例如當前最新版為1.5.1。
? 解壓:
tar xvf busybox-1.5.1.tar.bz2
? 配置
BusyBox的配置程序和Linux內核菜單配置方式簡直一模一樣,十分方便易用。使用make menuconfig命令就可以進行配置。
配置過程主要有幾點需要修改的:
l 因為我們要將BusyBox交叉編譯成ARM可執行程序放在開發板上執行,所以需要使用交叉編譯器arm-linux-gcc來編譯BusyBox。所以需要修改BusyBox根目錄下的Makefile,找到
ARCH ?= $(SUBARCH)
CROSS-COMPILE ?=
修改成ARM的配置,如下:
ARM ?= arm
CROSS-COMPILE ?= arm-linux-
l 在BusyBox配置界面里修改幾個配置:
Busybox Settings --->
Build Options
Build BusyBox as a static binary (no shared libs)
這個選項是一定要選擇的,這樣才能把BusyBox編譯成靜態鏈接的可執行文件,運行時才獨立於其他函數庫,否則必須要其他庫文件才能運行。
Busybox Settings --->
Installation Options
Don’t use /usr
這個選項也是要選上的,否則make install 后BusyBox將安裝在原系統的/usr下,這將覆蓋系統原有的命令。選擇這個選項后,make install后會在BusyBox目錄下生成一個叫_install的目錄,里面有BusyBox和指向他的鏈接。
Init Utilities --->
init
這個選項最好選上,這樣BusyBox就可以初始化腳本inittab,可以用來初始化Linux系統。
如果要讓BusyBox包含一個可以用於解釋Linux命令的shell,需要配置BusyBox的Shells選項里的內容:
Shells --->
這里可選的shell有多種,包括ash,hush,lash,msh。最好使用ash,因為它是功能最全也最類似於一般Linux系統中的BASH的。同時注意第一行的:
Choose your default shell (none) --->
這里需要回車進去選擇默認的Shell,例如選擇了ash后,第一行的內容就會變成:
Choose your default shell (ash) --->
這樣BusyBox才會生成sh的鏈接並且將這個sh指向對應的shell(ash)。
l 其他選項都是一些Linux基本命令選項,可以根據自己的需要選擇配置,第一次的話用默認的設置即可。
? 編譯
如果配置好了BusyBox,就可以使用make命令編譯了。
#make
#make install
默認情況下,make install完成后會在BusyBox目錄下創建一個新的本地子目錄 _install,其中包含了基本的 Linux 環境。在這個目錄中,會有一個鏈接到 BusyBox 的 linuxrc 程序。這個 linuxrc 程序在構建安裝盤或急救盤(允許提前進行模塊化的引導)時非常有用。同樣在這個目錄中,還有一個包含操作系統二進制文件的 /sbin 子目錄。還有一個包含用戶二進制文件的 /bin 目錄。在構建軟盤發行版或嵌入式初始 RAM 磁盤時,可以將這個 _install 目錄遷移到目標環境的根目錄中。
4、制作完整的根文件系統
BusyBox雖然為我們創建了Linux根系統中最基本的shell和一些常用命令,但是一個根文件系統還不只包含這些,還需要其它的一些內容。
? 創建一個比較完整的根文件系統目錄結構
本章第一節已經介紹了根文件系統中的一些目錄,這些目錄是Linux正常運行時所必需的。我們可以在BusyBox的_install基礎上創建完整的根文件系統目錄,一般步驟如下:
l 在PC上創建一個目標根文件系統的目錄,例如/rootfs,將BusyBox里的_install目錄里所有內容復制到這個文件夾里:cp –r _install /rootfs
l 在/rootfs下創建目錄etc/,dev/,lib/,tmp/,usr/,var/目錄,同時var/目錄里還需要創建var/run和var/log等目錄。
l 生成etc/里的設備文件,例如tty,console,fb(FrameBuffer),mtdblock(Memory Technology Device)等,這些設備文件是Linux很多驅動程序及就用程序正常的工作的基礎。這些設備文件都是與相應的硬件相聯系的,主要包含幾種信息:設備類型,主設備號,次設備號。
其中設備類型主要包括字符設備(Character Device)和塊設備(Block Device),字符設備主要字符的輸入輸出設備如鍵盤、鼠標等,塊設備主要指整塊數據的輸入輸出設備,如FLASH、硬盤等存儲設備,一般包含緩沖區機制。
主設備號用來區分不同種類的設備,而次設備號用來區分同一類型的多個設備。對於常用設備,Linux有約定俗成的編號,如硬盤的主設備號是3,而次設備對應到每個具體的設備上,一般在/proc/devices文件里可以找到相關信息。
對於一個已存在的設備文件可以通過ls –l 命令來獲取它的設備相關信息。
ls –l /dev/console
crw-rw---- 1 root root 5,1 Apr 14 23:08 /dev/console
可以看出第一個字母為c,這代表/dev/console是字符設備,若第一個字母為b,則為塊設備。而root之后的 5,1就分別為相應設備的主次設備號了。
這里需要強調一點,設備文件類似於配置文件,存儲的是一些設備信息,里面不包含特定平台下的指令,所以設備文件本身是平台無關的,也就是說在I386上創建的設備文件可以放在ARM的根文件系統,而可以被Linux正確識別的。
基本了解設備后,還需要如何創建它們,一般情況下可以使用mknod生成相應設備文件。mknod是Linux中用來創建設備文件的命令,格式如下:
mknode [–m MODE] NAME TYPE [MAJOR MINOR]
其中MODE用於指定設備文件的訪問權限。NAME為設備文件的文件名,TYPE為相應的設備類型(字符設備c,塊設備b等),MAJOR和MINOR分別為主次設備號。
例如要創建剛才的那個console命令可用如下的命令:
mknode –m 660 console c 5 1
設備文件的創建除了使用mknod命令,還可以使用MAKEDEV命令,MAKEDEV可以較方便的創建一系統的設備文件,一般的Linux發行版都有自帶,其基本格式如下:
MAKEDEV –d directory -m maxdevices device
其中directory為設備文件的目標存放文件夾,若不指定則為當前系統下的/dev里。maxdevices為最大的設備數,因為MAKEDEV一般會創建一種設備的一系列設備文件,一般從0開始編號,直到maxdevices,所以一般這個需要指定,要不會生成較多的相關設備文件,而一般我們是不需要這么多的。最后一個參數device為對應的設備文件名,包括tty,vt,mem,null,zero,fd,hd,audio,sound等。這些參數的詳細內容以及更多的參數選項,可以參考man手冊。
可以這樣創建硬盤的設備文件hd
MAKEDEV –d /rootfs/dev -m 2 hda
這條命令就會在/rootfs/dev目錄中創建hda和hda1兩個設備文件,指向第一塊硬盤和第一塊硬盤的第一個分區。
對於目標根文件系統中的設備文件,一般都應放在etc/目錄中,可以用如下幾種方法來獲取相應的設備文件:
ü 可以手動用mknod命令一個個的創建設備文件;
ü 可以使用MAKEDEV來創建設備文件;
ü 甚至可以直接拷貝PC系統中部分設備文件至目標根文件系統中。
l 構建lib/目錄。lib/目錄放的是Linux就用程序所需的庫文件,其實也是目標平台的指令代碼,所以這里的文件與etc/里不一樣,必須與相應的硬件平台相對應,例如i386里的庫文件放到ARM系統中就不能使用的,這點與Linux的可執行文件一樣。在PC機上使用ls /lib命令就可以看到很多.so結尾的庫文件,這些.so文件就是Linux的動態鏈接庫(類似於Windows下的DLL文件)。要注意的是.so文件名后綴還可能加上一些版本號標志例如.so.1,.so.1.2等都是動態鏈接庫。
ü 目標根文件系統中的庫文件從哪里來?
一般這些庫都是事先編譯好的,而且跟編譯器相關的(glibc等),例如我們使用arm-linux-gcc進行編譯則需要相應版本的一些庫文件,這些庫文件可以從編譯器所在的目錄里直接拷貝。對於ELDK開發包,可以從ELDK目錄下的arm/lib/目錄里復制相應文件。
ü 目標根文件系統需要哪些基本庫文件?
庫文件實際上是由其它可執行文件來調用的,所以庫文件的取舍是由根文件系統中所包含的可執行文件來決定的。但是要運行可執行文件,一般有幾個是系統必須的。它們是ld(ld-linux),libc,幾乎所有的可執行文件都需要調用到這兩個庫文件。
ld(ld-linux):ld-linux.so 實際上就是一個可執行程序。這是負責執行動態裝載的代碼。它從可執行程序讀取頭信息(ELF格式的),然后通過這些信息判斷必要的庫和需要裝載的庫。之后,執行動態鏈接,修改可執行程序和裝載的庫中的所有地址指針,使程序能夠運行。一般的文件名可能為ld-2.3.2.so,ld-linux.so.2,這點與編譯器和系統版本有關。
libc:libc.so.6 是以 ld-linux.so.2 為基礎架構而完成的動態鏈接庫,它幾乎負責了所有常用的標准 C 函數庫,例如 Linux 下寫的 Socket 程序,其中的connect()、bind()、send() .....之類的函數,都是由 libc.so.6 所提供的。
所以一個最基本的lib/目錄應該至少包含這ld-linux.so.2和libc.so.6這兩個文件。
ü 應用程序需要哪些庫文件?
前面已經說過動態鏈接庫是由應用程序(可執行文件)調用的,那對於一個特定的可執行文件是如何判斷它需要哪些庫文件的?一般可以編譯器的ldd命令來查看,例如arm-linux-gcc包含了arm-linux-ldd命令用來查看ARM可執行文件調用的動態鏈接庫。arm-linux-ldd的功能就是列出可執行文件及動態鏈接庫運行時需要的庫文件,例如,對於剛才所指的libc.so.6可以查找出其需要的動態鏈接庫。這里我們假設為ELDK里的libc。
# file /eldk/arm/libc.so.6
libc.so.6:symbolic link to `libc-2.3.5.so`
通過file命令看出這里的libc.so.6實際上是個符號連接,鏈接到libc-2.3.5.so,所以我們繼續追查libc-2.3.5.so:
#file libc-2.3.5.so
libc-2.3.5.so: ELF 32-bit LSB shared object, ARM, version 1(ARM), stripped
從這里可以明顯的看出這個是ARM的Shared Object,也就是ARM格式的動態鏈接庫。到這里可以判斷出libc.so.6是ARM指令的。下來看libc.so.6到底需要哪些庫文件:
#arm-linux-ldd libc.so.6
ld-linux.so.2 => not found
可以看出libc.so.6庫文件需要ld-linux.so.2這個動態鏈接庫(not found 說明在當前系統中未找到相應的庫,因為系統是i386而需要的是ARM格式,所以找不到)。
這樣通過arm-linux-ldd命令就可以確定各個程序所需的動態鏈接庫,然后根據需要放到lib/目錄里,就組成目標根文件系統的動態鏈接庫集合了。
l 若選擇了BusyBox的init模塊,則需要配置BusyBox的初始化文件。因為Linux系統加載根文件系統后需要執行一些配置以初始化整個Linux的工作環境及init程序和Shell等。這個文件就是etc/inittab。關於此文件的詳細內容可以查看man inittab。但是BusyBox的inittab格式與一般Linux下的inittab的格式是不同,所以直接拷貝PC機上/etc/inittab文件到BusyBox制作的根文件系統中是不能用的,那怎么辦呢?
BusyBox自帶了符合它的格式的inittab樣本文件,放在examples目錄下,主要內容包括:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
tty2::askfirst:-bin/sh
tty3::askfirst:-bin/sh
tty4::askfirst:-bin/sh
#Stuff to do when restarting the init process
::restart:/sbin/reboot
#Stuff to do before rebooting
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff –a
BusyBox的inittab的每一行格式如下:
<id>:<runlevels>:<action>:<process>
總共包含四項,每項間以”:”隔開。
第一二項<id>和<runlevels>在BusyBox都是忽略掉的,所以可以看到BusyBox提供的inittab樣本文件的所有項目都是以兩個冒號”::”開頭的。
第三項<action>為動作描述,可選項為sysinit, respawn, askfirst, wait, once, restart, ctrlaltdel 和 shutdown。其中大部分動作可以通過動作名直接理解它的作用。其中askfirst會在登錄shell前提示用戶,而respawn則不會提示。
第四項<process>指定了<action>動作應執行的腳本文件。
知道了格式,下面簡單的分析一下inittab樣本文件:
第一行::sysinit:/etc/init/rcS實際上指定了系統初始化(sysinit)時腳本為/etc/init/rcS,這個可以根據自己的需要更改的。
但是對於不同的終端設備的不同配置區別在於開頭的標志,例如對於tty2終端,則有對應的操作 tty2::askfirst:-bin/sh。此行的意思指對於tty2使用shell為/bin/sh,同時對askfirst(有提示信息再要求登陸)。 若對於某個特定的終端設備可以直接將前面的設備標志去掉,例如ttyS0, ttyS1等。
第二行::askfirst:-/bin/sh指定了系統第一個終端在加載shell為/bin/sh,而且在進入shell前會提示用戶。其它行請讀者自行分析。
l 在etc/目錄里除了inittab文件外,還需要其它的一些基本的文件,例如fstab、passwd、group、inputrc等,由於篇幅所限,不可能一一詳解,讀者可以參考其它書籍或者man手冊,對於一些文件讀者也可借用別的嵌入式根文件系統里的內容,然后在此基礎上進行修改以符合自己的系統。這里簡單介紹etc/里的幾個文件:
ü fstab:這個文件描述系統中各種文件系統的信息。在這個文件中,每個文件系統用一行來描述,在每一行中,用空格或TAB符號來分隔各個字段,文件中以*開頭的行是注釋信息。一般內容可能如下:
/dev/mtdblock2 / jffs2 defaults 0 0
none /tmp ramfs defaults 0 0
none /proc proc defaults 0 0
第一列(字段):設備名或者設備卷標名(一般為/dev里的對應的設備文件)
第二列(字段):設備掛載目錄 (例如上面的“/”或者“/tmp”)
第三列(字段):設備文件系統 (例如上面的“ext3”或者“vfat”)
第四列(字段):掛載參數 (具體可以查看幫助man mount)
對於已經掛載好的設備,例如上面的/dev/sda2,現在要改變掛載參數,這時可以不用卸載該設備,而可以使用下面的命令(沒有掛載的設備,remount 這個參數無效)
#mount /mnt -o remount,ro (改defaults為ro)
關於其它參數請參考man手冊。
第五列(字段):指明是否要備份。(0為不備份,1為要備份)
第六列(字段):指明自檢順序。 (0為不自檢,1或者2為要自檢,如果是根分區要設為1,其他分區只能是2)
ü passwd和group保存着Linux系統的用戶組和用戶名等,與硬件平台無關,為方便起見,可以從現有的Linux系統中拷貝過去即可。
etc/目錄里的配置文件較多,不可能一一解釋,請讀者在創建時多參考已有的系統。
5、總結
上面已經介紹一個根文件系統的創建過程,如果完整的按照上面的步驟做下來,應該就會在/rootfs下得到了一個相對完整的根文件系統,這個根文件系統主要BusyBox的bin/、sbin/目錄,etc/系統配置文件目錄以及lib/動態鏈接庫所在目錄等,這樣一個Linux應用程序可執行的最小環境基本已經搭成了。之后可以在這個根文件系統中添加所需的應用程序等等。
但是這個根文件系統又是怎么放到目標開發板里的呢?
通常的做法是將整個根文件系統打包成某種文件系統格式的映像,然后下載到目標開發板的存儲設備里(如FLASH等)。
用什么程序可以打包?支持幾種格式呢?
通常使用mkfs系統命令。mkfs命令可以生成指定文件系統類型的映像文件。對於不同的文件系統類型需要不同的mkfs命令,例如EXT2,EXT3類型的文件可以使用mkfs.ext2和mkfs.ext3等。而通常嵌入式開發板使用FLASH作為存儲設備,所以對應的文件系統類型一般為JFFS2,所以使用命令mkfs.jffs2命令,這個命令一般在開發板提供的工具有,也可以從網上搜索下載,這個命令一般是運行在PC系統上的,所以一般為I386可執行文件。
mkfs.jffs2的基本命令格式如下:
mkfs.jffs2 -r DIR -o FILE -e SIZE --pad=PADSIZE
其中DIR為要打包的文件夾,FILE為輸出的文件路徑,SIZE為每次擦除的塊大小(默認為64KB)。PADSIZE為填充大小,這個參數強制使目標文件大小至少為PADSIZE字節,若實際數據沒有這么大,則使用0xFF填充,這個參數很重要,在將映像寫入目標開發板時一般應與實際根文件系統大小相符(類似於總磁盤容量),這樣具有初始化的作用,若不相符合,在掛載這個JFFS2根文件系統時可能會出現一些問題。
下面給個簡單的例子,這個例子將/rootfs的這個根文件系統打包成文件rootfs.img,且總大小為1M(0x100000字節)。
mkfs.jffs2 -r /rootfs -o rootfs.img -e 0x40000 --pad=0x100000
做好映像文件后就可以將這個映像文件寫入目標板中,通常使用U-BOOT等BootLoader通過網卡下載到開發板內存中,然后再寫入開發板的FLASH里。在U-BOOT里若網卡驅動可用,通常用tftp下載,再使用相關的FLASH操作命令寫數據。