1. 程式人生 > >Linux 系統啟動過程(initrd部分) --- Linux boot process (initrd part)

Linux 系統啟動過程(initrd部分) --- Linux boot process (initrd part)

原帖:http://blog.csdn.net/ropyn/article/details/2489945

以前一直沒有研究過initrd部分,只是知道linux核心啟動後會首先由引導裝載器讀取initrd映像來啟動ramdisk.它的作用是雞和蛋問題的解決方案,即首先安裝一個記憶體盤作為臨時的root,然後載入其上的磁碟/網路磁碟驅動程式,從而找到真正的硬碟裝置/網路檔案系統,再掛載它為真正的root,從而進入整個linux world。但這裡面到底幹了些什麼,以及怎麼實現的沒有仔細研究,也認為比較簡單,直到有一天,第一次升級我的home box,linux2.4核心到linux2.6核心,在啟動過程中kernel panic -- 傻眼了。當時由於工作的緣故沒有深究下去,但那時第一次遇到這個問題。最有由於自己想搭建一個linux平臺,迫不得已只好開始仔細研究這個東西了。當然最後收穫還是頗豐,把過程簡單記錄一下,希望對其它人有幫助。 我一直使用redhat的系統,包括最近的FC。所以我主要以RH/FC的啟動過程為例進行說明。其他系統略有不同,但基本原理是一樣的。

initrd放在RH/FC系統的/boot目錄中,在grub,conf中指定。如:
initrd /initrd-2.6.23.1-42.fc8.img 注意這裡的/是指grub的root,那是grub的root命令所指磁碟的boot目錄下。

這個映像檔案在2.6.1.x核心以前的版本中是一個被壓縮的loop裝置檔案,但在2.6.1.x以後的核心支援它可以是一個被壓縮的cpio包了。如果是cpio包可以:

mkdir initrd && mv /boot/initrd-2.6.23.1-42.fc8.img initrd && cd initrd
gunzip -c < initrd-2.6.23.1-42.fc8.img | cpio -imd      #注意它將ramdisk內容解壓到inird當前目錄bin sbin

如果是loop裝置,必須通過mount命令載入它才能訪問:
mkdir /mnt/initrd
gunzip -c < initrd-2.6.23.1-42.fc8.img > initrd.loop
mount -o loop,rw initrd.loop /mnt/initrd

在initrd目錄中就是系統啟動時臨時root的內容。目錄下有一個init檔案,該檔案最為重要,它就是核心啟動後執行的第一個指令碼。實際上核心啟動後尋找的就是/init ; /sbin/init ;/bin/init,找到任何一個就執行它。整個的初始化從它開始。它的內容如下(FC8):

#!/bin/nash

mount -t proc /proc /proc
setquiet
echo Mounting proc filesystem
echo Mounting sysfs filesystem
mount -t sysfs /sys /sys
echo Creating /dev
mount -o mode=0755 -t tmpfs /dev /dev
mkdir /dev/pts
mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts
mkdir /dev/shm
mkdir /dev/mapper
echo Creating initial device nodes
mknod /dev/null c 1 3
mknod /dev/zero c 1 5
mknod /dev/systty c 4 0
mknod /dev/tty c 5 0
mknod /dev/console c 5 1
mknod /dev/ptmx c 5 2
mknod /dev/rtc c 10 135
mknod /dev/tty0 c 4 0
mknod /dev/tty1 c 4 1
mknod /dev/tty2 c 4 2
mknod /dev/tty3 c 4 3
mknod /dev/tty4 c 4 4
mknod /dev/tty5 c 4 5
mknod /dev/tty6 c 4 6
mknod /dev/tty7 c 4 7
mknod /dev/tty8 c 4 8
mknod /dev/tty9 c 4 9
mknod /dev/tty10 c 4 10
mknod /dev/tty11 c 4 11
mknod /dev/tty12 c 4 12
mknod /dev/ttyS0 c 4 64
mknod /dev/ttyS1 c 4 65
mknod /dev/ttyS2 c 4 66
mknod /dev/ttyS3 c 4 67
echo Setting up hotplug.
hotplug
echo Creating block device nodes.
mkblkdevs
echo "Loading ehci-hcd.ko module"
insmod /lib/ehci-hcd.ko
echo "Loading ohci-hcd.ko module"
 insmod /lib/ohci-hcd.ko
echo "Loading uhci-hcd.ko module"
insmod /lib/uhci-hcd.ko
mount -t usbfs /proc/bus/usb /proc/bus/usb
echo "Loading mbcache.ko module"
insmod /lib/mbcache.ko
echo "Loading jbd.ko module"
insmod /lib/jbd.ko
echo "Loading ext3.ko module"
insmod /lib/ext3.ko
echo "Loading scsi_mod.ko module"
insmod /lib/scsi_mod.ko
echo "Loading sd_mod.ko module"
insmod /lib/sd_mod.ko
echo "Loading scsi_transport_spi.ko module"
insmod /lib/scsi_transport_spi.ko
echo "Loading mptbase.ko module"
insmod /lib/mptbase.ko
echo "Loading mptscsih.ko module"
insmod /lib/mptscsih.ko
echo "Loading mptspi.ko module"
insmod /lib/mptspi.ko
echo "Loading dm-mod.ko module"
insmod /lib/dm-mod.ko
echo "Loading dm-mirror.ko module"
insmod /lib/dm-mirror.ko
echo "Loading dm-zero.ko module"
insmod /lib/dm-zero.ko
echo "Loading dm-snapshot.ko module"
insmod /lib/dm-snapshot.ko
echo "Loading libata.ko module"
insmod /lib/libata.ko
echo "Loading ata_piix.ko module"
insmod /lib/ata_piix.ko
echo Waiting for driver initialization.
stabilized --hash --interval 250 /proc/scsi/scsi
echo Making device-mapper control node
mkdmnod
insmod /lib/scsi_wait_scan.ko
rmmod scsi_wait_scan
mkblkdevs
echo Scanning logical volumes
lvm vgscan --ignorelockingfailure
echo Activating logical volumes
lvm vgchange -ay --ignorelockingfailure  VolGroup00
resume /dev/VolGroup00/LogVol01
echo Creating root device.
mkrootdev -t ext3 -o defaults,ro /dev/VolGroup00/LogVol00
echo Mounting root filesystem.
mount /sysroot
echo Setting up other filesystems.
setuproot
echo Switching to new root and running init.
switchroot
echo Booting has failed.
sleep -1

第一行#!/bin/nash是一個redhat自己的微型直譯器。不是bash。裡面的命令像,mount, echo, mkrootdev等等並不是shell 命令。而是nash的內部命令。能做基本的mount等的功能。可能是為了減小initrd的體積而設計的吧。只有insmod,lvm是外部命令。如果nash在當前的路徑上找到名字相同的外部命令,它會只執行外部的命令,不執行內部版本。nash的文件很不全,man nash得到很少的資訊,如mkrootdev,而且某些命令的文件是錯的,如mkrootdev,mount,以及部分命令根本就沒有文件,如mkblkdevs,setuproot。我被迫讀nash原始碼才瞭解到。nash是隨mkinitrd包一起發行的,裡面有nash的原始碼。

前面的部分功能比較簡單。看名字就基本都可以理解了,mount命令載入各種核心檔案系統proc,sys等,mknod建立系統啟動所必須的/dev裝置節點(其實大多數不是必須的,只有null,console,zero可能是核心所必須的)。hotplug開始監聽系統匯流排熱插拔的磁碟等uevent事件。mkblkdevs將這些熱插拔的磁碟節點建立到/dev下。接著insmod開始安裝usb,block裝置,scsi裝置,ide裝置,device-mapper的裝置驅動,隨後系統匯流排就會收到這些事件,mkblkdevs又會把他們的節點建立到/dev下。我們的硬碟多數是在這個階段被找到並建立了相應的裝置節點。然後是載入啟動邏輯卷管理,使我們的裝置能使用邏輯卷名稱。這一步可以不要的,在啟動階段硬碟完全可以用LABEL或/dev/sda1這樣的名字來表示。

最重要的部分從mkrootdev開始。mkrootdev的工作流程是這樣:

首先從/proc/cmdline中查詢root=核心引數,再解析mkrootdev後面的-t 和-o引數,如果-t是nfs,而且,-o中包含dhcp,那麼root的相關資訊從dhcp主機取得,如果-o選項不包含dhcp或者-t不是nfs,則從root=核心引數取真正root檔案系統的位置,如果沒有root=引數,使用mkrootdev後面的裝置作為root裝置。如果是-t nfs那麼直接新增入口到/etc/fstab檔案,包括-t的檔案系統型別和-o的選項。如果-t指定的檔案系統不是nfs,那麼它建立/dev/root節點,它的Major,Minor裝置號使用root=引數的裝置號(如果有),或mkrootdev後面的裝置的裝置號。

所以,mkrootdev -t ext3 -o defaults,ro /dev/VolGroup00/LogVol00這行,會將一條入口寫入記憶體中的/etc/fstab,如: /dev/root  /sysroot ext3 defaults,ro 0 0 ;注意裝置是/dev/root.而不是/dev/VolGroup00/LogVol00,但他們的裝置號是一樣的。

緊接著後面的mount /sysroot就會讀取/etc/fstab並載入剛剛mkrootdev寫入的這條入口所指定的裝置,這就是載入/dev/root /sysroot下。這時我們的真正的root才被載入到了系統中。nash的mount要求,如果只有一個引數,以它作為root載入點,且它必須是/sysroot(會同fstab中的做比較)。如果,有載入點和裝置引數,-t -o必須也被提供,mount丟棄/etc/fstab中的入口的內容。而使用引數提供的資訊載入root裝置。若如本例,至此/sysroot下是真正的root分割槽。/是initrd的記憶體盤。

setuproot並不理睬後面的引數,以/sysroot作為我們的root。安裝所有的子分割槽到/sysroot下。也就是建立我們的根檔案系統數,如果我們的root不是由單個分割槽組成的話。這些資訊從/sysroot/etc/fstab.sys中讀取。如果它不存在,就從initrd的/etc/fstab.sys中讀取。如果仍然不存在,就建立預設的檔案系統樹:

 掛載(Bind掛載方式)/proc 到/sysroot/proc. /sys到/sysroot/sys

最後switchroot解除安裝/dev,/proc,/sys檔案系統,掛載(移動掛載方式)/sysroot到/下面.,然後解除安裝initrd的/。開啟/dev/console到描述符3,將stdin,stdout,stderr全部定向到3(console),分析核心引數,尋找init=,如果有以它作為init程式執行,否則,執行預設:搜尋第一個找到的/sbin/init;/etc/init;/bin/init;/bin/sh執行,這就是所有程序的父程序0

至此initrd完成了啟動過程,switchroot不會返回,sleep -1將永遠不會執行,除非上面的switchroot不能找到一個init和sh。

綜觀此過程,如果不用nash是完全可以實現的,但initrd裡面必須copy很多命令進去。且必須copy 一個shell:sh/bash。在載入模組部分完全可以加入其他的模組,比如,網路smb,nfs,或光碟檔案系統模組,以這些種類的檔案系統作為根。來搭建無盤工作站系統,或者live CD系統。也可以做救援磁碟。來尋找,加載出現故障的root分割槽。在除錯過程中,可以啟動一個互動式shell(sh -i),方便我們直接執行命令。nash中,可以用exec --nokill /bin/bash -i -l命令來給我們一個shell。當然,你要先copy需要的檔案(包括bash等)進入initrd的/bin目錄中,這裡給出如何自己壓縮initrd的命令:

用於大於2.6.1.x以後的核心:

cd initrd
find . | cpio -o -H newc | gzip -c > initrd.img

用於舊核心:

dd if=/dev/zero of=initrd bs=300k count=1
mke2fs -F -m0 initrd
mount -t ext2 -o loop initrd /mnt/initrd
cp -R initrd/* /mnt/initrd
umount /mnt/initrd
gzip -9 -c initrd > initrd.img