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