initrd,既initial RAM disk(Linux出事RAM磁盤),實在系統引導過程中掛載的一個臨時根文件系統。用來支持兩階段的引導過程。initrd文件中包含了各種可執行的程序和驅動,用來掛在真實的根文件系統,然後將這個虛擬的文件系統卸載。
現在的initrd映像變成了一個經過壓縮的cpio歸檔文件。不再使用loop設備來將initrd作爲壓縮映像掛在,而是可以將其作爲cpio歸檔文件來使用。查看cpio歸檔文件內容:#cp /boot/initrd-2.6.32-358.el6.x86_64kdump.img /tmp/initrd-2.6.32-358.el6.x86_64kdump.img.gz 不同版本名字可能不一樣。雖然initrd文件是以.img結尾,但是還是一個壓縮的文件。#gunzip initrd initrd-2.6.32-358.el6.x86_64kdump.img.gz 然後利用cpio -i -d <initrd-2.6.32-358.el6.x86_64kdump.img導出來文件。最小的文件系統裏包括:/bin init /lib64 /proc /sys /usr /dev /mnt /sbin /sysroot /var /etc /lib /modules /scriptfns /tmp
構建initrd:
1.爲了創建 initrd,我們最開始創建了一個空文件,這使用了/dev/zero(一個由零組成的碼流)作爲輸入,並將其寫入到ramdisk.img 文件中。所生成的文件大小是 4MB(4000 個 1K大小的塊)。然後使用 mke2fs 命令在這個空文件上創建了一個 ext2(即 secondextended)文件系統。現在這個文件變成了一個 ext2 格式的文件系統,我們使用loop設備將這個文件掛載到 /mnt/initrd上了。在這個掛載點上,我們現在就有了一個目錄,它以 ext2文件系統的形式呈現出來,我們可以對自己的 initrd文件進行拼裝了。接下來的腳本提供了這種功能。
2.下一個步驟是創建構成根文件系統所需要的子目錄:/bin、/sys、/dev 和 /proc。這裏只列出了所需要的目錄(例如沒有庫),但是其中包含了很多功能。
3.ext2文件系統的替代品。儘管 ext2 是一種通用的 Linux文件系統格式,但是還有一些替代品可以減小initrd映像文件以及所掛載上來的文件系統的大小。這種文件系統的例子有 romfs(ROM文件系統)、cramfs(壓縮 ROM文件系統)和 squashfs(高度壓縮只讀文件系統)。如果我們需要暫時將數據寫入文件系統中,ext2可以很好地實現這種功能。最後,e2compr 是 ext2文件系統驅動程序的一個擴展,可以支持在線壓縮。
4.爲了可以使用根文件系統,我們使用了BusyBox。這個工具是一個單一映像,其中包含了很多在 Linux系統上通常可以找到的工具(例如 ash、awk、sed、insmod等)。BusyBox的優點是它將很多工具打包成一個文件,同時還可以共享它們的通用元素,這樣可以極大地減少映像文件的大小。這對於嵌入式系統來說非常理想。將BusyBox 映像從自己的源目錄中拷貝到自己根目錄下的 /bin 目錄中。然後創建了很多符號鏈接,它們都指向 BusyBox工具。BusyBox 會判斷所調用的是哪個工具,並執行這個工具的功能。我們在這個目錄中創建了幾個鏈接來支持 init腳本(每個命令都是一個指向 BusyBox 的鏈接。)
5.下一個步驟是創建幾個特殊的設備文件。我從自己當前的 /dev 子目錄中直接拷貝了這些文件,這使用了 -a 選項(歸檔)來保留它們的屬性。
6.倒數第二個步驟是生成linuxrc 文件。在內核掛載 RAM磁盤之後,它會查找init 文件來執行。如果沒有找到 init 文件,內核就會調用linuxrc文件作爲自己的啓動腳本。我們在這個文件中實現對環境的基本設置,例如掛載 /proc文件系統。除了 /proc 之外,我還掛載了 /sys文件系統,並向終端打印一條消息。最後,我們調用了 ash(一個 Bourne Shell的克隆),這樣就可以與根文件系統進行交互了。linuxrc 文件然後使用 chmod 命令修改成可執行的。
7.最後,我們的根文件系統就完成了。我們將其卸載掉,然後使用 gzip 對其進行壓縮。所生成的文件(ramdisk.img.gz)被拷貝到 /boot 子目錄中,這樣就可以通過 GNU GRUB 對其進行加載了。
#!/bin/bash
# Housekeeping...
rm -f /tmp/ramdisk.img
rm -f /tmp/ramdisk.img.gz
# Ramdisk Constants
RDSIZE=4000
BLKSIZE=1024
# Create an emptyramdisk image
dd 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 populate
mount /tmp/ramdisk.img /mnt/initrd -t ext2 -oloop=/dev/loop0
# Populate the filesystem (subdirectories)
mkdir /mnt/initrd/bin
mkdir /mnt/initrd/sys
mkdir /mnt/initrd/dev
mkdir /mnt/initrd/proc
# Grabbusyboxand create the symbolic links
pushd /mnt/initrd/bin
cp /usr/local/src/busybox-1.1.1/busybox.
ln -sbusyboxash
ln -sbusyboxmount
ln -sbusyboxecho
ln -sbusyboxls
ln -sbusyboxcat
ln -sbusyboxps
ln -sbusyboxdmesg
ln -sbusyboxsysctl
popd
# Grab the necessary dev files
cp -a /dev/console /mnt/initrd/dev
cp -a /dev/ramdisk /mnt/initrd/dev
cp -a /dev/ram0 /mnt/initrd/dev
cp -a /dev/null /mnt/initrd/dev
cp -a /dev/tty1 /mnt/initrd/dev
cp -a /dev/tty2 /mnt/initrd/dev
# Equate sbin with bin
pushd /mnt/initrd
ln -s bin sbin
popd
# Create the init file
cat >> /mnt/initrd/linuxrc << EOF
#!/bin/ash
echo
echo "Simple initrd is active"
echo
mount -t proc /proc /proc
mount -t sysfs none /sys
/bin/ash --login
EOF
chmod +x /mnt/initrd/linuxrc
# Finish up...
umount /mnt/initrd
gzip -9 /tmp/ramdisk.img
cp /tmp/ramdisk.img.gz /boot/ramdisk.img.gz
測試定製的初始化RAM磁盤
新的 initrd 映像現在已經在 /boot目錄中了,因此下一個步驟是使用默認的內核來對其進行測試。現在我們可以重新啓動 Linux 系統了。在出現 GRUB 界面時,按 C 鍵啓動GRUB 中的命令行工具。我們現在可以與 GRUB 進行交互,從而定義要加載哪個內核和 initrd映像文件。kernel命令讓我們可以指定內核文件,initrd 命令可以用來指定 initrd 映像文件。在定義好這些參數之後,就可以使用 boot命令來引導內核了。
GNU GRUB version 0.95 (638K lower / 97216K upper memory)
[ Minimal BASH-like line editing is supported. For the first word, TAB
lists possible command completions. Anywhere else TAB lists the possible
completions of a device/filename. ESC at any time exits.]
grub> kernel /bzImage-2.6.1
[Linux-bzImage, setup=0x1400, size=0x29672e]
grub> initrd /ramdisk.img.gz
[Linux-initrd @ 0x5f2a000, 0xb5108 bytes]
grub> boot
Uncompressing Linux... OK, booting the kernel.
壓縮initrd
在構建嵌入式系統時,我們可能希望將 initrd映像文件做得儘可能小,這其中有一些技巧需要考慮。首先是使用 BusyBox(本文中已經展示過了)。BusyBox 可以將數 MB 的工具壓縮成幾百 KB。
在這個例子中,BusyBox映像是靜態鏈接的,因此它不需要其他庫。然而,如果我們需要標準的 C 庫(我們自己定製的二進制可能需要這個庫),除了巨大的 glibc之外,我們還有其他選擇。第一個較小的庫是 uClibc,這是爲對空間要求非常嚴格的系統準備的一個標準 C 庫。另外一個適合空間緊張的環境的庫是dietlib。要記住我們需要使用這些庫來重新編譯想在嵌入式系統中重新編譯的二進制文件,因此這需要額外再做一些工作(但是這是非常值得的)。