什麼是初始 RAM磁碟。
初始 RAM磁碟(initrd)是在實際根檔案系統可用之前掛載到系統中的一個初始根檔案系統。initrd與核心綁定在一起,並作為核心引導過程的一部分進行載入。核心然後會將這個 initrd檔案作為其兩階段引導過程的一部分來載入模組,這樣才能稍後使用真正的檔案系統,並掛載實際的根檔案系統。
initrd中包含了實現這個目標所需要的目錄和可執行程式的最小集合,例如將核心模組載入到核心中所使用的 insmod 工具。
在案頭或伺服器 Linux 系統中,initrd是一個臨時的檔案系統。其生存周期很短,只會用作到真實檔案系統的一個橋樑。在沒有存放裝置的嵌入式系統中,initrd是永久的根檔案系統。本文將對這兩種情況進行探索。
initrd剖析
initrd 映像中包含了支援 Linux 系統兩階段引導過程所需要的必要可執行程式和系統檔案。
根據我們啟動並執行 Linux 的版本不同,建立初始 RAM 磁碟的方法也可能會有所不同。在 Fedora Core 3之前,initrd 是使用 loop裝置 來構建的。loop裝置 是一個裝置驅動程式,利用它可以將檔案作為一個塊裝置掛載到系統中,然後就可以查看這個檔案系統中的內容了。在您的核心中可能並沒有loop 裝置,不過這可以通過核心組態工具(makemenuconfig)選擇 DeviceDrivers > Block Devices > Loopback DeviceSupport 來啟用。我們可以按照下面的方法來查看loop 裝置的內容(initrd 檔案的名字可能會稍有不同):
清單 1. 查看 initrd 的內容(適用於FC3 之前的版本)
# mkdir temp ; cd temp# cp /boot/initrd.img.gz .# gunzip initrd.img.gz# mount -t ext2 -o loop initrd.img /mnt/initrd# ls -la /mnt/initrd# |
現在我們就可以查看 /mnt/initrd 子目錄中的內容了,這就代表了 initrd 檔案的內容。注意,即使您的 initrd映像檔案不是以 .gz 結尾,它也可能是一個壓縮檔,您可以給這個檔案添加上 .gz 尾碼,然後再使用 gunzip對其進行解壓。
從 Fedora Core 3 開始,預設的 initrd 映像變成了一個經過壓縮的 cpio 歸檔檔案。我們不用再使用 loop裝置來將 initrd 作為壓縮映像進行掛載,而是可以將其作為 cpio 歸檔檔案來使用。要查看 cpio歸檔檔案的內容,可以使用下面的命令:
清單 2. 查看 initrd 的內容(適用於FC3 及其以後的版本)
# mkdir temp ; cd temp# cp /boot/initrd-2.6.14.2.img initrd-2.6.14.2.img.gz# gunzip initrd-2.6.14.2.img.gz# cpio -i --make-directories < initrd-2.6.14.2.img# |
結果會產生一個很小的根檔案系統,如清單 3 所示。在 ./bin目錄中有一組很少但卻非常必要的應用程式,包括nash(即 not ashell,是一個指令碼解譯器)、insmod(用來載入核心模組)和 lvm(邏輯卷管理工具)。
清單 3. 預設的 Linux initrd目錄結構
# ls -la#drwxr-xr-x 10 root root 4096 May 7 02:48 .drwxr-x--- 15 root root 4096 May 7 00:54 ..drwxr-xr-x 2 root root 4096 May 7 02:48 bindrwxr-xr-x 2 root root 4096 May 7 02:48 devdrwxr-xr-x 4 root root 4096 May 7 02:48 etc-rwxr-xr-x 1 root root 812 May 7 02:48 init-rw-r--r-- 1 root root 1723392 May 7 02:45 initrd-2.6.14.2.imgdrwxr-xr-x 2 root root 4096 May 7 02:48 libdrwxr-xr-x 2 root root 4096 May 7 02:48 loopfsdrwxr-xr-x 2 root root 4096 May 7 02:48 proclrwxrwxrwx 1 root root 3 May 7 02:48 sbin -> bindrwxr-xr-x 2 root root 4096 May 7 02:48 sysdrwxr-xr-x 2 root root 4096 May 7 02:48 sysroot# |
清單 3 中比較有趣的是 init 檔案就在根目錄中。與傳統的 Linux 引導過程類似,這個檔案也是在將 initrd 映像解壓到RAM 磁碟中時被調用的。在本文稍後我們將來探索這個問題。
建立 initrd所使用的工具 cpio 命令 使用 cpio 命令,我們可以對cpio 檔案進行操作。cpio 是一種檔案格式,它簡單地使用檔案頭將一組檔案串接在一起。cpio 檔案格式可以使用 ASCII和二進位檔案。為了保證可移植性,我們可以使用 ASCII格式。為了減小檔案大小,我們可以使用二進位的版本。
下面讓我們回到最開始,來看一下 initrd 映像最初是如何構建的。對於傳統的 Linux 系統來說,initrd 映像是在Linux 構建過程中建立的。有很多工具,例如 mkinitrd,都可以用來使用必要的庫和模組自動構建initrd,從而用作與真實的根檔案系統之間的橋樑。mkinitrd 工具實際上就是一個 shell指令碼,因此我們可以看到它究竟是如何來實現這個結果的。還有一個 YAIRD(即Yet Another Mkinitrd)工具,可以對 initrd 構建過程的各個方面進行定製。
手工構建定製的初始 RAM磁碟
由於在很多基於 Linux 的嵌入式系統上沒有硬碟,因此 initrd 也會作為這種系統上的永久根檔案系統使用。清單 4顯示了如何建立一個 initrd 映像檔案。我使用了一個標準的 Linux案頭,這樣您即使沒有嵌入式平台,也可以按照下面的步驟來執行了。除了交叉編譯,其他概念(也適用於 initrd的構建)對於嵌入式平台都是相同的。
清單 4. 建立定製 initrd的工具(mkird)
#!/bin/bash# Housekeeping...rm -f /tmp/ramdisk.imgrm -f /tmp/ramdisk.img.gz# Ramdisk ConstantsRDSIZE=4000BLKSIZE=1024# Create an empty ramdisk imagedd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE# Make it an ext2 mountable file system/sbin/mke2fs -F -m 0 -b $BLKSIZE /tmp/ramdisk.img $RDSIZE# Mount it so that we can populatemount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0# Populate the filesystem (subdirectories)mkdir /mnt/initrd/binmkdir /mnt/initrd/sysmkdir /mnt/initrd/devmkdir /mnt/initrd/proc# Grab busybox and create the symbolic linkspushd /mnt/initrd/bincp /usr/local/src/busybox-1.1.1/busybox .ln -s busybox ashln -s busybox mountln -s busybox echoln -s busybox lsln -s busybox catln -s busybox psln -s busybox dmesgln -s busybox sysctlpopd# Grab the necessary dev filescp -a /dev/console /mnt/initrd/devcp -a /dev/ramdisk /mnt/initrd/devcp -a /dev/ram0 /mnt/initrd/devcp -a /dev/null /mnt/initrd/devcp -a /dev/tty1 /mnt/initrd/devcp -a /dev/tty2 /mnt/initrd/dev# Equate sbin with binpushd /mnt/initrdln -s bin sbinpopd# Create the init filecat >> /mnt/initrd/linuxrc << EOF#!/bin/ashechoecho "Simple initrd is active"echomount -t proc /proc /procmount -t sysfs none /sys/bin/ash --loginEOFchmod +x /mnt/initrd/linuxrc# Finish up...umount /mnt/initrdgzip -9 /tmp/ramdisk.imgcp /tmp/ramdisk.img.gz /boot/ramdisk.img.gz |
為了建立 initrd,我們最開始建立了一個空檔案,這使用了 /dev/zero(一個由零組成的碼流)作為輸入,並將其寫入到ramdisk.img 檔案中。所產生的檔案大小是 4MB(4000 個 1K 大小的塊)。然後使用 mke2fs 命令在這個空檔案上建立了一個 ext2(即second extended)檔案系統。現在這個檔案變成了一個 ext2 格式的檔案系統,我們使用 loop 裝置將這個檔案掛載到/mnt/initrd 上了。在這個掛載點上,我們現在就有了一個目錄,它以 ext2 檔案系統的形式呈現出來,我們可以對自己的initrd 檔案進行拼裝了。接下來的指令碼提供了這種功能。下一個步驟是建立構成根檔案系統所需要的子目錄:/bin、/sys、/dev和 /proc。這裡只列出了所需要的目錄(例如沒有庫),但是其中包含了很多功能。
為了可以使用根檔案系統,我們使用了 BusyBox。這個工具是一個單一映像,其中包含了很多在 Linux系統上通常可以找到的工具(例如 ash、awk、sed、insmod 等)。BusyBox的優點是它將很多工具打包成一個檔案,同時還可以共用它們的通用元素,這樣可以極大地減少映像檔案的大小。這對於嵌入式系統來說非常理想。將BusyBox 映像從自己的來源目錄中拷貝到自己根目錄下的 /bin 目錄中。然後建立了很多符號連結,它們都指向 BusyBox工具。BusyBox 會判斷所調用的是哪個工具,並執行這個工具的功能。我們在這個目錄中建立了幾個連結來支援 init指令碼(每個命令都是一個指向 BusyBox 的連結。)
下一個步驟是建立幾個特殊的裝置檔案。我從自己當前的 /dev 子目錄中直接拷貝了這些檔案,這使用了 -a 選項(歸檔)來保留它們的屬性。
倒數第二個步驟是產生 linuxrc 檔案。在核心掛載 RAM 磁碟之後,它會尋找 init 檔案來執行。如果沒有找到init 檔案,核心就會調用linuxrc 檔案作為自己的啟動指令碼。我們在這個檔案中實現對環境的基本設定,例如掛載 /proc 檔案系統。除了 /proc之外,我還掛載了 /sys 檔案系統,並向終端列印一條訊息。最後,我們調用了ash(一個 Bourne Shell的複製),這樣就可以與根檔案系統進行互動了。linuxrc 檔案然後使用 chmod 命令修改成可執行檔。
最後,我們的根檔案系統就完成了。我們將其卸載掉,然後使用 gzip 對其進行壓縮。所產生的檔案(ramdisk.img.gz)被拷貝到/boot 子目錄中,這樣就可以通過 GNU GRUB 對其進行載入了。
要構建初始 RAM 磁碟,我們可以簡單地調用 mkird,這樣就會自動建立這個映像檔案,並將其拷貝到/boot 目錄中。