嵌入式Linux是由一個幾百KB的Linux內核和一個根據需要制定的文件系統所構成了, 由於Linux是開放源代碼的操作系統,所以在嵌入式領域有著非常廣闊的前景,並已經廣泛應用在許多手機、PDA、mp3播放器等許多電子產品中。本文將介紹一種兩張軟盤上的Linux系統,它可以當作系統應急修復盤、路由器或防火牆等許多地方,通過對它的研究,也可以加深對嵌入式系統的理解。 一.前言 嵌入式Linux是由一個幾百KB的Linux內核和一個根據需要制定的文件系統所構成了, 由於Linux是開放源代碼的操作系統,所以在嵌入式領域有著非常廣闊的前景,並已經廣泛應用在許多手機、PDA、MP3播放器等許多電子產品中。本文將介紹一種兩張軟盤上的Linux系統,它可以當作系統應急修復盤、路由器或防火牆等許多地方,通過對它的研究,也可以加深對嵌入式系統的理解。 二.Linux啟動過程 所有的PC機在加電之後,BIOS會尋找到啟動盤第一個扇區,並將其復制到RAM中來執行它,對於兩種不同的啟動方式,這個扇區通常含有兩種不同的代碼:引導程序(比如Lilo或Grub等)的代碼,引導程序會幫助定位內核的位置。內核的代碼,這通常是從軟盤啟動時使用的引導的方式。對於前者,通常需要內核支持initrd。如果是後者,使用的Boot Loader就是arch/i386/boot/bootsect.S。當內核被編譯的時候,這段執行代碼就被鏈接到內核image的最開始的地方。這樣很容易就能只要把內核復制到起始位置為第一個扇區的軟盤上就能得到可自啟動的軟盤。內核會初始化設備驅動和內部的數據結構,之後它會到一個特定的位置――Ramdisk Word來獲得根文件系統的位置。內核必須知道去那裡尋找這個根文件系統,否則它將停機。 在使用軟盤啟動的方式時,內核可以把一個壓縮的文件系統釋放到RAM中,稱之為Ramdisk,這是一個內存區域,但內核會把它當作磁盤一樣使用。 本文中介紹的例子使用Grub做為引導程序,並使用initrd來輔助Linux的啟動。兩張軟盤分別命名為bootldr盤和rootfs盤,在bootldr盤中內容為grub、內核、initrd,rootfs盤中是壓縮過的根文件系統。系統啟動時bootldr盤的Grub定位並執行內核,然後內核解開initrd,並執行linuxrc文件,這個文件負責提示用戶更換rootfs盤並將其中內容解壓至內存中,然後執行剛剛解壓的init繼續啟動過程。 為了方便理解這個例子,先介紹目錄結構如下: /home/papaya ├─bootldr/ │ ├─grub/ │ ├─kernel/ │ │ ├─images/ │ │ └─linux-2.4.21/ │ └─initrd/ │ ├─mkinitrd.sh │ ├─local/ │ └─ramdisk/ ├─rootfs/ │ ├─mkrootfs.sh │ ├─ramdisk/ │ └─local/ └─lib/ 三.定制Grub引導程序 插入一張軟盤,然後將其格式化,然後加載到/mnt/floppy #mke2fs /dev/fd0 #mount -t ext2 /dev/fd0 /mnt/floppy -o loop 在其中創建/boot/grub目錄 #mkdir -p /mnt/floppy/boot/grub 將系統中/boot/grub下的device.map, stage1, stage2 復制到/mnt/floppy/boot/grub中,然後在/mnt/floppy/boot/grub目錄下創建grub.conf文件: default=0 timeout=10 title Floppy Linux kernel (fd0)/bzImage root=/dev/ram0 initrd (fd0)/initrd.gz 然後創建一個鏈接 #ln -s grub.conf menu.lst 執行 /sbin/grub --batch --device-map=/dev/null <<EOF device (fd0) /dev/fd0 root (fd0) setup (fd0) quit EOF 這樣grub就被安裝到bootldr盤上了。 四.定制Linux內核 由於軟盤大小的限制,內核應盡可能只包含必要的一些支持,對於本文中的例子一定要選上initrd支持。比如如果做為系統修復盤的話,必要的支持包括:IDE,PCI,和需要的文件系統類型等等就可以了,而沒有必要網絡支持,當然,如果做為路由器或者防火牆的話,網絡支持是必要的,而其他的這可相應的刪除掉。 #make [xconfig menUConfig config] #make bzImage 如果添加了模塊的支持,還需要 #make modules 之後就得到了內核鏡像bzImage。如果bzImage的大小超出了軟盤的限制,就需要重新再來配置一下。將編譯好的bzImage放到bootldr盤的根目錄下,如果把bzImage改了名字,要注意與grub.conf中的名字一致。 五.制定initrd 在initrd/local目錄下建立bin, dev, etc, lib, proc, sysroot, usr目錄。其中dev目錄下包括必要的設備文件,比如tty, ram, console等等, bin中必要的可執行文件有bzip2, chroot, cp, cpio, dd, echo, mount, pivot_root, readkey, sh, test等。Busybox提供了其中大部分。 bzip2, dd, cpio用來解壓縮第二張軟盤上的內容,chroot, pivot_root用來轉換根目錄。 編輯initrd/local/linuxrc文件: #!/bin/sh 把sysroot目錄mount到一塊內存上,並建立tmpfs文件系統。 echo "Mounting new root filsystem ..." mount tmpfs /sysroot -t tmpfs cd /sysroot 下面的readkey是一個很簡單的程序,當啟動過程執行到這裡的時候暫停,等待換入第二章軟盤,然後接受任意鍵輸入繼續執行啟動過程。這個小程序讀者可以自己實現,要注意的是最好使用靜態鏈接。 echo " " echo -en "Insert the second disk and press ANY key..." readkey > /dev/null echo " " 將第二章軟盤上的內容解壓到sysroot目錄(內存)中。 echo "Loading root-archive from floppy ..." dd if=/dev/fd0 bs=1k bzip2 -d cpio -idv 下面將initrd中的文件copy到sysroot/bin目錄下,這樣可以把根文件系統中一部分內容放到initrd(第一張軟盤)中,因為軟盤容量有限,當第一張軟盤空間有剩余,而第二章軟盤空間緊張的時候這會非常有用。 echo "Copying:" for file in bzip2 chroot cp cpio echo readkey; do echo -en " "; echo -n $file cp /bin/$file ./bin/$file done 下面將/目錄設定為當前目錄,即sysroot,並執行剛剛從rootfs盤中解壓出來的init。 echo " " echo "Pivoting / ..." pivot_root . mnt/initrd echo "Starting init process..." exec chroot . /sbin/init <dev/console >/dev/console 2>&1 echo -en"Something went wrong ..." /bin/sh /mnt/initrd/bin/sh 當initrd所有必須的文件都放到bootldr/initrd/local目錄下之後,就可以執行bootldr/initrd/mkinitrd.sh來創建initrd鏡像文件。mkinitrd.sh的內容為: #!/bin/sh mount -t ext2 /dev/fd0 /mnt/floppy rm -f /mnt/floppy/initrd.gz rm -f initrd.gz 取4M大小的內存塊格式化為ext2格式,並將其mount到bootldr/initrd/ramdisk上。 dd if=/dev/zero of=/dev/ram9 bs=1k count=4096 mke2fs /dev/ram9 mount -t ext2 /dev/ram9 ramdisk/ 把local中的文件復制到ramdisk目錄中,也就是那塊內存中。 cp -R local/* ramdisk/ umount ramdisk 將內存中的內容壓縮為initrd.gz,並復制到bootldr盤中 dd if=/dev/ram9 bs=1k gzip -v9 > initrd.gz cp initrd.gz /mnt/floppy/ umount /mnt/floppy 這樣,bootldr盤就完成了。 六.定制根文件系統 一個根文件系統需要包含支持Linux系統運行的所有文件。通常包括: 基本的文件系統結構 基本的目錄: /dev, /proc, /bin, /sbin, /etc, /usr, /tmp等。 基本的工具: sh, ls, cp, cd, mv等。 基本的配置文件: rc, inittab, fstab等。 設備: /dev/hd*, /dev/tty*, /dev/fd0, /dev/ram*, /dev/console等. 基本的運行庫。 Busybox和Tinylogin是在嵌入式系統上常用的工具包,它們包含了上面提到的常用的工具和目錄結構等,而且經過重新改寫後所生成的代碼比普通的Linux系統上的工具要小的多。 編輯Busybox的Config.h文件,選擇自己需要的工具。修改Busybox和Tinylogin的Makefile文件,制定它們使用靜態鏈接方式(DOSTATIC=true),這樣就不需要在生成的系統中添加運行庫了。將編譯好的Busybox和Tinylogin文件放到rootfs/local中。 在rootfs/local中在自己創建下面幾個目錄:dev/, tmp/, etc/, proc/ 可以將系統中/dev下的設備復制到這個目錄下,只需要復制必要的就可以了,例如: #cp -dpR /dev/tty[0-9] /mnt/rootfs/dev #cp -dpR /dev/ram* /mnt/rootfs/dev 但是要注意一定要包含必要的接各設備/dev/console, /dev/kmem, /dev/mem, /dev/tty, /dev/ram0, /dev/null等。 etc/目錄下包含了目標系統運行所必須的配置文件,它包括的內容依賴與目標系統所要運行的程序。最低限度,它包括下面幾個文件:inittab、rc、fstab、passwd、group、shadow、termcap等。做為init進程的參數,inittab可以非常簡單,僅需要包括下面幾行即可: ::sysinit:/etc/rc ::askfirst:/bin/login tty2::askfirst:/bin/login tty3::askfirst:/bin/login tty4::askfirst:/bin/login ::ctrlaltdel:/sbin/reboot ::restart:/sbin/init ::shutdown:/bin/umount -a -r ::shutdown:/sbin/swapoff -a 其中sysinit指明系統初始化腳本rc。rc所包含內容也可以非常少: #!/bin/sh /bin/mount -av /bin/umount /mnt/initrd /bin/hostname papaya fstab的內容為: none /proc proc defaults 0 0 none /tmp tmpfs defaults 0 0 其他的配置文件可以從原來的系統中獲得,然後修剪掉不必要的內容即可。 現在在/mnt/rootfs中已經包含了運行一個最低限度Linux系統所必須的所有文件和工具,下面需要將它們壓縮成一個文件系統了。插入rootfs軟盤並執行bootldr/rootfs/mkrootfs.sh #!/bin/sh rm -f rootfs.cpio.bz2 dd if=/dev/zero of=/dev/ram0 bs=1k count=4096 mke2fs /dev/ram0 mount -t ext2 /dev/ram0 ramdisk/ cp -R local/* ramdisk/ cd ramdisk/ find . -depth -print cpio -o > ../rootfs.cpio cd .. bzip2 rootfs.cpio umount ramdisk dd if=rootfs.cpio.bz2 of=/dev/fd0 bs=1k OK,rootfs盤也完成了,可以重啟機器驗證了。 七.其他方法 將內核與文件系統進行整合,如果不用Grub引導還有兩種選擇,不過根文件系統就不能象上面那樣打包再壓縮, 也不再使用initrd。把所有根文件系統文件放到一個目錄中(比如上面的rootfs/local),然後執行 dd if=/dev/zero of=/dev/ram0 bs=1k count=4096 mke2fs /dev/ram0 mount -t ext2 /dev/ram0 ramdisk/ cp -R local/* ramdisk/ umount ramdisk dd if=/dev/ram0 bs=1k gzip -v9 > rootfs.gz 1.將內核與文件系統放置在一張軟盤上 確定內核的大小和的大小之合沒有超出軟盤的限制。記住內核的大小,然後將內核寫到軟盤上: #dd if=bzImage of=/dev/fd0 bs=1k 353+1 records in 353+1 records out 之後,設置根設備為軟盤本身,並且設置根以讀寫方式裝載 #rdev /dev/fd0 /dev/fd0 #rdev -R /dev/fd0 0 上面這個例子表示dd寫了353個完整記錄和一個部分記錄到軟盤上,因此內核占用了軟盤的前354個記錄塊。記住這個數字,然後設置內核的Ramdisk Word。Ramdisk Word可以通過rdev命令設置,它的內容為:
如果15位設置的話,內核在加載文件系統之前會進行提示,這在下面將內核與文件系統盤分開的情況時是必要的。 對於上面的情況,需要在0-10位指出ramdisk的偏移,並將14位置1,所以得出的ramdisk word十進制表示為:355 + 2^14 = 355 + 16384 = 16739 #rdev -r /dev/fd0 16739 之後 #dd if=rootfs.gz of=/dev/fd0 bs=1k seek=354 這樣一張同時包含內核和文件系統的軟盤就成功了。 2.內核與文件系統分別占用一張軟盤 與上面一樣 #dd if=bzImage of=/dev/fd0 bs=1k #rdev /dev/fd0 /dev/fd0 #rdev -R /dev/fd0 0 不同的是ramdisk word為 0 + 2^14 + 2^15 = 49152 #rdev -r /dev/fd0 49152 然後換零一張軟盤 #dd if=rootfs.gz of=/dev/fd0 bs=1k 內核、根文件系統、引導程序之間的整合方法有很多,他們各有各自的特點,有待讀者自己思考。 八.前景 按照本文方法所構造的Linux系統雖然還不完善,但通過逐步的改進,將能做出一個有價值的系統來。真正的Linux嵌入式系統根據不同的應用方向通常還包括圖形用戶界面或者是網絡浏覽器等應用程序。值得慶幸的是很多OpenSource的項目提供了所需要的組件,結合這些優秀的開源項目,就可以形成一套真正的嵌入式Linux操作系統。