因為我們要考慮從 Flash 盤進行啟動,所以我們選擇用 LILO 作為我們的 Boot Loader,而不選用 GRUB。這是考慮到 GRUB 有較強的對硬盤和文件系統的識別能力,而 Flash 盤到底不是標准的硬盤,並且我們選用的文件系統 GRUB 又不一定認識,搞不好的話 GRUB 反會弄巧成拙。而 LILO 就簡單的多了,它在硬盤開始的 MBR 寫入一個小程序,這個小程序不經過文件系統,直接從硬盤扇區號,讀出 Kernel Image 裝入內存。這樣,保險系數就大大增加。並且也給了我們自由選用文件系統的余地。那麼,我們要如何安裝 LILO 呢?
首先,我們要找一塊普通的 800M 左右的 IDE 硬盤,連在目標機器的 IDE 線上。這樣在我們的目標機器上,IDE1 上掛的是 Flash 盤,IDE2 上掛的是一塊工作硬盤。我們用標准的步驟在 IDE2 的標准硬盤上裝上一個 Debian GNU/Linux 系統。當然,如果讀者朋友們手頭沒有 Debian,也可以裝 Red Hat 系統。裝好工作系統之後,要首先做一些裁減工作,把不必要的 Service 和 X Window 等等東西都刪掉。這樣做的目的是增進系統啟動速度,因為我們在後面的工作中,肯定要不停的重新啟動機器,所以啟動速度對我們的工作效率是很關鍵的。
裝好工作系統之後,在 Falsh 盤上做一個 Ext2 文件系統,這個用 mke2fs 這個命令就可以完成。由於 Flash 盤是接在 IDE1 上的,所以在 Linux 裡面,它的身份是 /dev/hda。本文作者在操作的時候,把整個 Flash 盤劃分了一個整個的分區,所以,調用 mke2fs 的時候,處理的是 /dev/hda1。讀者朋友們應該可以直接在 /dev/hda 上做一個 Ext2 文件系統,而不用事先分區。
在 Flash 盤上做好了文件系統之後,就可以把一個編譯好的內核映像文件 vmlinuz 拷貝到 Flash 盤上了。注意,必須要先把這個 vmlinuz 映像文件拷貝到 Flash 盤上,然後才能在 Flash 盤上安裝 LILO。不然的話,LILO 到時候可是會 LILILILI 打結巴的,因為它會找不到 Kernel Image 在 Flash 盤上的位置的,那樣的話 Flash 盤也就啟動不起來了。還有,如果讀者朋友們在 Flash 盤上用的是一個壓縮的文件系統的話,到時候 LILO 也會出問題,它雖然能正確的找到 Kernel Image 在硬盤上的起始位置,但是它卻沒有辦法處理被文件系統重新壓縮過的這個 Kernel Image,不知道該如何把它展開到內存中去。
把 Kernel Image 拷貝過去以後,我們就可以動手編輯一份 lilo.conf 文件,這份文件可以就放在工作系統上就行了。但是注意在 lilo.conf 中索引的文件名的路徑可要寫對。這些路徑名都是在工作系統上看上去的路徑名。比如,如果 Flash 盤 Mount 在 /mnt 目錄下面,那麼,在 lilo.conf 中,vmlinuz 的路徑名就是 /mnt/vmlinuz。注意這一點千萬不要搞錯。不然的話,如果一不小心把工作系統的 LILO 給破壞掉了,那就麻煩了。編輯好了 lilo.conf,然後再運行 lilo 命令,注意,要告訴它用這個新的 lilo.conf 文件,而不要用 /etc/lilo.conf。
安裝好 LILO 之後,我們可以立即重新啟動,測試一下。首先在 BIOS 裡面,設置成從 IDE1 開始啟動,如果我們看到 LILO 的提示符,按回車後還能看到 Kernel 輸出的消息,這就算是 LILO 的安裝成功了。記得這個操作的方法,以後每次我們更新 Flash 盤上的 Kernel Image,都記得要更新 LILO。也就是說,要重新運行一遍 lilo 命令。
編譯內核
試驗成功 LILO 的安裝以後,我們開始考慮編譯一個新的內核。當然,要編譯新的內核,我們首先要進入我們的工作系統。這裡有兩個辦法進入工作系統,一是在 BIOS 裡面設置從 IDE2 啟動,當然,這就要求當初安裝工作系統的時候,要把 LILO 安裝在 /dev/hdb 上;另一個辦法是還是從 IDE1 啟動,不改變 BIOS 的設置,但是在看到 LILO 的提示符的時候,要鍵入 linux root=/dev/hdb1,最前面的 linux 是在 lilo.conf 裡面定義的一個 entry,我們只采用這個 entry 所指定的 Kernel Image,但是用 /dev/hdb1 作為 root 文件系統。兩個辦法可能有的時候一個比另一個好,更方便一些。這就要看具體的情況了。不過,它們的設置並不是互相沖突的。
在編譯內核的時候,由於我們的內核是只有一台機器使用的,所以我們應該對它的情況了如指掌;另外一方面,為了減低不必要的復雜性,我們決定不用 kernel module 的支持,而把所有需要的東西直接編譯到內核的裡面。這樣編譯出來的內核,在一台普通的 586 主板上,把所有必要的功能都加進去,一般也不到 800K 字節。所以,這個辦法是可行的。而且減低了 init scripts 的復雜程度。從運行方面來考慮,由於需要的 kernel 代碼反正是要裝載到內存中的,所以並不會引起內存的浪費。
在我們的目標平台上,我們希望使用 USB 存儲設備。還有一點要注意的,就是對 Frame buffer 的支持。這主要是為了支持 XFree86。一般說來,如果我們的顯卡是 XFree86 直接支持的,那當然最好,也就不需要 frame buffer 的內核支持。但是如果 XFree86 不支持我們的顯卡,我們可以考慮用 VESA 模式。但是 XFree86 的 VESA 卡支持運行起來不太漂亮,還有安全方面的問題,有時在啟動和退出 X Window 的時候會出現花屏。所以我們可以采用 kernel 的 vesa 模式的 frame buffer,然後用 xfree86 的 linux frame buffer 的驅動程序。這樣一般就看不到花屏的現象了,而且安全方面也沒有任何問題。
devfs 也是我們感興趣的話題。如果 kernel 不使用 devfs,那麼系統上的 root 文件系統就要有 /dev 目錄下面的所有內容。這些內容可以用 /dev/MAKEDEV 腳本來建立,也可以用 mknod 手工一個一個來建。這個方法有其自身的好處。但是它的缺點是麻煩,而且和 kernel 的狀態又並不一致。相反的,如果使用了 devfs,我們就再也不用擔心 /dev 目錄下面的任何事情了。/dev 目錄下面的項目會有 kernel 的代碼自己負責。實際使用起來的效果,對內存的消耗並不明顯。所以我們選擇 devfs。
busybox
有了 LILO 和 kernel image 之後,接下來,我們要安排 root 文件系統。由於 flash 盤的空間只有 16M 字節,可以說,這是對我們最大的挑戰。這裡首先要向大家介紹小型嵌入式 Linux 系統安排 root 文件系統時的一個常用的利器:BusyBox。
Busybox 是 Debian GNU/Linux 的大名鼎鼎的 Bruce Perens 首先開發,使用在 Debian 的安裝程序中。後來又有許多 Debian developers 貢獻力量,這其中尤推 busybox 目前的維護者 Erik Andersen,他患有癌症,可是卻是一名優秀的自由軟件開發者。
Busybox 編譯出一個單個的獨立執行程序,就叫做 busybox。但是它可以根據配置,執行 ash shell 的功能,以及幾十個各種小應用程序的功能。這其中包括有一個迷你的 vi 編輯器,系統不可或缺的 /sbin/init 程序,以及其他諸如 sed, ifconfig, halt, reboot, mkdir, mount, ln, ls, echo, cat ... 等等這些都是一個正常的系統上必不可少的,但是如果我們把這些程序的原件拿過來的話,它們的體積加在一起,讓人吃不消。可是 busybox 有全部的這麼多功能,大小也不過 100K 左右。而且,用戶還可以根據自己的需要,決定到底要在 busybox 中編譯進哪幾個應用程序的功能。這樣的話,busybox 的體積就可以進一步縮小了。
使用 busybox 也很簡單。只要建一個符號鏈接,比方 ln -s /bin/busybox /bin/ls,那麼,執行 /bin/ls 的時候,busybox 就會執行 ls 的功能,也會按照 ls 的方式處理命令行參數。又比如 ln -s /bin/busybox /sbin/init,這樣我們就有了系統運行不可或缺的 /sbin/init 程序了。當然,這裡的前提是,你在 busybox 中編譯進去了這兩個程序的功能。
這裡面要提出注意的一點是,busybox 的 init 程序所認識的 /etc/inittab 的格式非常簡單,而且和常規的 inittab 文件的格式不一樣。所以讀者朋友們在為這個 busybox 的 init 寫 inittab 的時候,要注意一下不同的語法。至於細節,就不在我們這裡多說了,請大家參考 Busybox 的用戶手冊。
從啟動到進入 shell
busybox 安裝好以後,我們就可以考慮重新啟動,一直到進入 shell 提示符了。這之前,我們要准備一下 /etc 目錄下的幾個重要的文件,而且要把 busybox 用到的 library 也拷貝過來。
用 ldd 命令,後面跟要分析的二進制程序的路徑名,就可以知道一個二進制程序,或者是一個 library 文件之間的互相依賴關系,比如 busybox 就依賴於 libc.so 和 ld-linux.so ,我們有了這些知識,就可把動手把所有需要的 library 拷貝到 flash 盤上。由於我們的 flash 盤說大不大,說小倒也不小,有 16M 字節之多。我們直接就用 Glibc 的文件也沒有太多問題。如果讀者朋友們有特殊的需要,覺得 Glibc 太龐大了的話,可以考慮用 uClibc,這是一個非常小巧的 libc 庫,功能當然沒有 Glibc 全,但是足夠一個嵌入式系統使用了。本文就不再介紹 uClibc 了。
庫程序拷貝過來以後,我們就可以考慮系統啟動的步驟了。啟動的時候,先是 lilo,接下來就是 kernel,kernel 初始化之後,