天天看點

使用BusyBox制作根檔案系統【轉】

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

繼續閱讀