轉(zhuǎn)自IBM開發(fā)者網(wǎng)站
李 大治 ([email protected]), 軟件工程師
Linux 的 initrd 技術(shù)是一個(gè)非常普遍使用的機(jī)制,linux2.6 內(nèi)核的 initrd 的文件格式由原來(lái)的文件系統(tǒng)鏡像文件轉(zhuǎn)變成了 cpio 格式,變化不僅反映在文件格式上, linux 內(nèi)核對(duì)這兩種格式的 initrd 的處理有著截然的不同。本文首先介紹了什么是 initrd 技術(shù),然后分別介紹了 Linux2.4 內(nèi)核和 2.6 內(nèi)核的 initrd 的處理流程。最后通過(guò)對(duì) Linux2.6 內(nèi)核的 initrd 處理部分代碼的分析,使讀者可以對(duì) initrd 技術(shù)有一個(gè)全面的認(rèn)識(shí)。為了更好的閱讀本文,要求讀者對(duì) Linux 的 VFS 以及 initrd 有一個(gè)初步的了解。
.什么是 Initrd
initrd 的英文含義是 boot loader initialized RAM disk,就是由 boot loader 初始化的內(nèi)存盤。在 linux內(nèi)核啟動(dòng)前, boot loader 會(huì)將存儲(chǔ)介質(zhì)中的 initrd 文件加載到內(nèi)存,內(nèi)核啟動(dòng)時(shí)會(huì)在訪問(wèn)真正的根文件系統(tǒng)前先訪問(wèn)該內(nèi)存中的 initrd 文件系統(tǒng)。在 boot loader 配置了 initrd 的情況下,內(nèi)核啟動(dòng)被分成了兩個(gè)階段,第一階段先執(zhí)行 initrd 文件系統(tǒng)中的"某個(gè)文件",完成加載驅(qū)動(dòng)模塊等任務(wù),第二階段才會(huì)執(zhí)行真正的根文件系統(tǒng)中的 /sbin/init 進(jìn)程。這里提到的"某個(gè)文件",Linux2.6 內(nèi)核會(huì)同以前版本內(nèi)核的不同,所以這里暫時(shí)使用了"某個(gè)文件"這個(gè)稱呼,后面會(huì)詳細(xì)講到。第一階段啟動(dòng)的目的是為第二階段的啟動(dòng)掃清一切障愛,最主要的是 加載根文件系統(tǒng)存儲(chǔ)介質(zhì)的驅(qū)動(dòng)模塊。我們知道根文件系統(tǒng)可以存儲(chǔ)在包括IDE、SCSI、USB在內(nèi)的多種介質(zhì)上,如果將這些設(shè)備的驅(qū)動(dòng)都編譯進(jìn)內(nèi)核,可 以想象內(nèi)核會(huì)多么龐大、臃腫。
Initrd 的用途主要有以下四種:
1. linux 發(fā)行版的必備部件
linux 發(fā)行版必須適應(yīng)各種不同的硬件架構(gòu),將所有的驅(qū)動(dòng)編譯進(jìn)內(nèi)核是不現(xiàn)實(shí)的,initrd 技術(shù)是解決該問(wèn)題的關(guān)鍵技術(shù)。Linux 發(fā)行版在內(nèi)核中只編譯了基本的硬件驅(qū)動(dòng),在安裝過(guò)程中通過(guò)檢測(cè)系統(tǒng)硬件,生成包含安裝系統(tǒng)硬件驅(qū)動(dòng)的 initrd,無(wú)非是一種即可行又靈活的解決方案。
2. livecd 的必備部件
同 linux 發(fā)行版相比,livecd 可能會(huì)面對(duì)更加復(fù)雜的硬件環(huán)境,所以也必須使用 initrd。
3. 制作 Linux usb 啟動(dòng)盤必須使用 initrd
usb 設(shè)備是啟動(dòng)比較慢的設(shè)備,從驅(qū)動(dòng)加載到設(shè)備真正可用大概需要幾秒鐘時(shí)間。如果將 usb 驅(qū)動(dòng)編譯進(jìn)內(nèi)核,內(nèi)核通常不能成功訪問(wèn) usb 設(shè)備中的文件系統(tǒng)。因?yàn)樵趦?nèi)核訪問(wèn) usb 設(shè)備時(shí), usb 設(shè)備通常沒有初始化完畢。所以常規(guī)的做法是,在 initrd 中加載 usb 驅(qū)動(dòng),然后休眠幾秒中,等待 usb設(shè)備初始化完畢后再掛載 usb 設(shè)備中的文件系統(tǒng)。
4. 在 linuxrc 腳本中可以很方便地啟用個(gè)性化 bootsplash。
.Linux2.4內(nèi)核對(duì) Initrd 的處理流程
為了使讀者清晰的了解Linux2.6內(nèi)核initrd機(jī)制的變化,在重點(diǎn)介紹Linux2.6內(nèi)核initrd之前,先對(duì)linux2.4內(nèi)核的 initrd進(jìn)行一個(gè)簡(jiǎn)單的介紹。Linux2.4內(nèi)核的initrd的格式是文件系統(tǒng)鏡像文件,本文將其稱為image-initrd,以區(qū)別后面介紹 的linux2.6內(nèi)核的cpio格式的initrd。 linux2.4內(nèi)核對(duì)initrd的處理流程如下:
1. boot loader把內(nèi)核以及/dev/initrd的內(nèi)容加載到內(nèi)存,/dev/initrd是由boot loader初始化的設(shè)備,存儲(chǔ)著initrd。
2. 在內(nèi)核初始化過(guò)程中,內(nèi)核把 /dev/initrd 設(shè)備的內(nèi)容解壓縮并拷貝到 /dev/ram0 設(shè)備上。
3. 內(nèi)核以可讀寫的方式把 /dev/ram0 設(shè)備掛載為原始的根文件系統(tǒng)。
4. 如果 /dev/ram0 被指定為真正的根文件系統(tǒng),那么內(nèi)核跳至最后一步正常啟動(dòng)。
5. 執(zhí)行 initrd 上的 /linuxrc 文件,linuxrc 通常是一個(gè)腳本文件,負(fù)責(zé)加載內(nèi)核訪問(wèn)根文件系統(tǒng)必須的驅(qū)動(dòng), 以及加載根文件系統(tǒng)。
6. /linuxrc 執(zhí)行完畢,真正的根文件系統(tǒng)被掛載。
7. 如果真正的根文件系統(tǒng)存在 /initrd 目錄,那么 /dev/ram0 將從 / 移動(dòng)到 /initrd。否則如果 /initrd 目錄不存在, /dev/ram0 將被卸載。
8. 在真正的根文件系統(tǒng)上進(jìn)行正常啟動(dòng)過(guò)程 ,執(zhí)行 /sbin/init。 linux2.4 內(nèi)核的 initrd 的執(zhí)行是作為內(nèi)核啟動(dòng)的一個(gè)中間階段,也就是說(shuō) initrd 的 /linuxrc 執(zhí)行以后,內(nèi)核會(huì)繼續(xù)執(zhí)行初始化代碼,我們后面會(huì)看到這是 linux2.4 內(nèi)核同 2.6 內(nèi)核的 initrd 處理流程的一個(gè)顯著區(qū)別。
.Linux2.6 內(nèi)核對(duì) Initrd 的處理流程
linux2.6 內(nèi)核支持兩種格式的 initrd,一種是前面第 3 部分介紹的 linux2.4 內(nèi)核那種傳統(tǒng)格式的文件系統(tǒng)鏡像-image-initrd,它的制作方法同 Linux2.4 內(nèi)核的 initrd 一樣,其核心文件就是 /linuxrc。另外一種格式的 initrd 是 cpio 格式的,這種格式的 initrd 從 linux2.5 起開始引入,使用 cpio 工具生成,其核心文件不再是 /linuxrc,而是 /init,本文將這種 initrd 稱為 cpio-initrd。盡管 linux2.6 內(nèi)核對(duì) cpio-initrd和 image-initrd 這兩種格式的 initrd 均支持,但對(duì)其處理流程有著顯著的區(qū)別,下面分別介紹 linux2.6 內(nèi)核對(duì)這兩種 initrd 的處理流程。
1. boot loader 把內(nèi)核以及 initrd 文件加載到內(nèi)存的特定位置。
2. 內(nèi)核判斷initrd的文件格式,如果是cpio格式。
3. 將initrd的內(nèi)容釋放到rootfs中。
4. 執(zhí)行initrd中的/init文件,執(zhí)行到這一點(diǎn),內(nèi)核的工作全部結(jié)束,完全交給/init文件處理。
1. boot loader把內(nèi)核以及initrd文件加載到內(nèi)存的特定位置。
2. 內(nèi)核判斷initrd的文件格式,如果不是cpio格式,將其作為image-initrd處理。
3. 內(nèi)核將initrd的內(nèi)容保存在rootfs下的/initrd.image文件中。
4. 內(nèi)核將/initrd.image的內(nèi)容讀入/dev/ram0設(shè)備中,也就是讀入了一個(gè)內(nèi)存盤中。
5. 接著內(nèi)核以可讀寫的方式把/dev/ram0設(shè)備掛載為原始的根文件系統(tǒng)。
6. 如果/dev/ram0被指定為真正的根文件系統(tǒng),那么內(nèi)核跳至最后一步正常啟動(dòng)。
7. 執(zhí)行initrd上的/linuxrc文件,linuxrc通常是一個(gè)腳本文件,負(fù)責(zé)加載內(nèi)核訪問(wèn)根文件系統(tǒng)必須的驅(qū)動(dòng), 以及加載根文件系統(tǒng)。
8. /linuxrc執(zhí)行完畢,常規(guī)根文件系統(tǒng)被掛載
9. 如果常規(guī)根文件系統(tǒng)存在/initrd目錄,那么/dev/ram0將從/移動(dòng)到/initrd。否則如果/initrd目錄不存在, /dev/ram0將被卸載。
10. 在常規(guī)根文件系統(tǒng)上進(jìn)行正常啟動(dòng)過(guò)程 ,執(zhí)行/sbin/init。
通過(guò)上面的流程介紹可知,Linux2.6內(nèi)核對(duì)image-initrd的處理流程同linux2.4內(nèi)核相比并沒有顯著的變化, cpio-initrd的處理流程相比于image-initrd的處理流程卻有很大的區(qū)別,流程非常簡(jiǎn)單,在后面的源代碼分析中,讀者更能體會(huì)到處理的簡(jiǎn)捷。
.cpio-initrd同image-initrd的區(qū)別與優(yōu)勢(shì)
沒有找到正式的關(guān)于cpio-initrd同image-initrd對(duì)比的文獻(xiàn),根據(jù)筆者的使用體驗(yàn)以及內(nèi)核代碼的分析,總結(jié)出如下三方面的區(qū)別,這些區(qū)別也正是cpio-initrd的優(yōu)勢(shì)所在:
cpio-initrd的制作非常簡(jiǎn)單,通過(guò)兩個(gè)命令就可以完成整個(gè)制作過(guò)程
?
#假設(shè)當(dāng)前目錄位于準(zhǔn)備好的initrd文件系統(tǒng)的根目錄下
bash# find . | cpio -c -o > ../initrd.img
bash# gzip ../initrd.img
?
而傳統(tǒng)initrd的制作過(guò)程比較繁瑣,需要如下六個(gè)步驟
#假設(shè)當(dāng)前目錄位于準(zhǔn)備好的initrd文件系統(tǒng)的根目錄下
bash# dd if=/dev/zero of=../initrd.img bs=512k count=5
bash# mkfs.ext2 -F -m0 ../initrd.img
bash# mount -t ext2 -o loop ../initrd.img? /mnt
bash# cp -r? * /mnt
bash# umount /mnt
bash# gzip -9 ../initrd.img
本文不對(duì)上面命令的含義作細(xì)節(jié)的解釋,因?yàn)楸疚闹饕榻B的是linux內(nèi)核對(duì)initrd的處理,對(duì)上面命令不理解的讀者可以參考相關(guān)文檔。
通過(guò)上面initrd處理流程的介紹,cpio-initrd的處理流程顯得格外簡(jiǎn)單,通過(guò)對(duì)比可知cpio-initrd的處理流程在如下兩個(gè)方面得到了簡(jiǎn)化:
1. cpio-initrd并沒有使用額外的ramdisk,而是將其內(nèi)容輸入到rootfs中,其實(shí)rootfs本身也是一個(gè)基于內(nèi)存的文件系統(tǒng)。這樣就省掉了ramdisk的掛載、卸載等步驟。
2. cpio-initrd啟動(dòng)完/init進(jìn)程,內(nèi)核的任務(wù)就結(jié)束了,剩下的工作完全交給/init處理;而對(duì)于image-initrd,內(nèi)核在執(zhí)行完 /linuxrc進(jìn)程后,還要進(jìn)行一些收尾工作,并且要負(fù)責(zé)執(zhí)行真正的根文件系統(tǒng)的/sbin/init。通過(guò)圖1可以更加清晰的看出處理流程的區(qū)別:
1內(nèi)核對(duì)cpio-initrd和image-initrd處理流程示意圖
如 圖1所示,cpio-initrd不再象image-initrd那樣作為linux內(nèi)核啟動(dòng)的一個(gè)中間步驟,而是作為內(nèi)核啟動(dòng)的終點(diǎn),內(nèi)核將控制權(quán)交給 cpio-initrd的/init文件后,內(nèi)核的任務(wù)就結(jié)束了,所以在/init文件中,我們可以做更多的工作,而不比擔(dān)心同內(nèi)核后續(xù)處理的銜接問(wèn)題。 當(dāng)然目前linux發(fā)行版的cpio-initrd的/init文件的內(nèi)容還沒有本質(zhì)的改變,但是相信initrd職責(zé)的增加一定是一個(gè)趨勢(shì)。
.linux2.6內(nèi)核initrd處理的源代碼分析
上面簡(jiǎn)要介紹了Linux2.4內(nèi)核和2.6內(nèi)核的initrd的處理流程,為了使讀者對(duì)于Linux2.6內(nèi)核的initrd的處理有一個(gè)更加深 入的認(rèn)識(shí),下面將對(duì)Linuxe2.6內(nèi)核初始化部分同initrd密切相關(guān)的代碼給予一個(gè)比較細(xì)致的分析,為了講述方便,進(jìn)一步明確幾個(gè)代碼分析中使用 的概念:
rootfs: 一個(gè)基于內(nèi)存的文件系統(tǒng),是linux在初始化時(shí)加載的第一個(gè)文件系統(tǒng),關(guān)于它的進(jìn)一步介紹可以參考文獻(xiàn)[4]。
initramfs: initramfs同本文的主題關(guān)系不是很大,但是代碼中涉及到了initramfs,為了更好的理解代碼,這里對(duì)其進(jìn)行簡(jiǎn)單的介紹。Initramfs 是在 kernel 2.5中引入的技術(shù),實(shí)際上它的含義就是:在內(nèi)核鏡像中附加一個(gè)cpio包,這個(gè)cpio包中包含了一個(gè)小型的文件系統(tǒng),當(dāng)內(nèi)核啟動(dòng)時(shí),內(nèi)核將這個(gè) cpio包解開,并且將其中包含的文件系統(tǒng)釋放到rootfs中,內(nèi)核中的一部分初始化代碼會(huì)放到這個(gè)文件系統(tǒng)中,作為用戶層進(jìn)程來(lái)執(zhí)行。這樣帶來(lái)的明顯的好處是精簡(jiǎn)了內(nèi)核的初始化代碼,而且使得內(nèi)核的初始化過(guò)程更容易定制。Linux
cpio-initrd: 前面已經(jīng)定義過(guò),指linux2.6內(nèi)核使用的cpio格式的initrd。
image-initrd: 前面已經(jīng)定義過(guò),專指?jìng)鹘y(tǒng)的文件鏡像格式的initrd。
realfs: 用戶最終使用的真正的文件系統(tǒng)。
內(nèi)核的初始化代碼位于 init/main.c 中的 static int init(void * unused)函數(shù)中。同initrd的處理相關(guān)部分函數(shù)調(diào)用層次如下圖,筆者按照這個(gè)層次對(duì)每一個(gè)函數(shù)都給予了比較詳細(xì)的分析,為了更好的說(shuō)明,下面列 出的代碼中刪除了同本文主題不相關(guān)的部分:
2 initrd相關(guān)代碼的調(diào)用層次關(guān)系圖
init函數(shù)是內(nèi)核所有初始化代碼的入口,代碼如下,其中只保留了同initrd相關(guān)部分的代碼。
?
static int init(void * unused){
[1]????? populate_rootfs();
????????
[2]????? if (sys_access((const char __user *) "/init", 0) == 0)
???????????????? execute_command = "/init";
???????? else
???????????????? prepare_namespace();
[3]????? if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
???????????????? printk(KERN_WARNING "Warning: unable to open an initial console.\n");
???????? (void) sys_dup(0);
???????? (void) sys_dup(0);
[4]????? if (execute_command)
???????????????? run_init_process(execute_command);
???????? run_init_process("/sbin/init");
???????? run_init_process("/etc/init");
???????? run_init_process("/bin/init");
???????? run_init_process("/bin/sh");
???????? panic("No init found.? Try passing init= option to kernel.");
}
?
代碼[1]:populate_rootfs函數(shù)負(fù)責(zé)加載initramfs和cpio-initrd,對(duì)于populate_rootfs函數(shù)的細(xì)節(jié)后面會(huì)講到。
代碼[2]:如果rootfs的根目錄下中包含/init進(jìn)程,則賦予execute_command,在init函數(shù)的末尾會(huì)被執(zhí)行。否則執(zhí)行prepare_namespace函數(shù),initrd是在該函數(shù)中被加載的。
代碼[3]:將控制臺(tái)設(shè)置為標(biāo)準(zhǔn)輸入,后續(xù)的兩個(gè)sys_dup(0),則復(fù)制標(biāo)準(zhǔn)輸入為標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出。
代碼[4]:如果rootfs中存在init進(jìn)程,就將后續(xù)的處理工作交給該init進(jìn)程。其實(shí)這段代碼的含義是如果加載了cpio-initrd 則交給cpio-initrd中的/init處理,否則會(huì)執(zhí)行realfs中的init。讀者可能會(huì)問(wèn):如果加載了cpio-initrd, 那么realfs中的init進(jìn)程不是沒有機(jī)會(huì)運(yùn)行了嗎?確實(shí),如果加載了cpio-initrd,那么內(nèi)核就不負(fù)責(zé)執(zhí)行realfs的init進(jìn)程了, 而是將這個(gè)執(zhí)行任務(wù)交給了cpio-initrd的init進(jìn)程。解開fedora core4的initrd文件,會(huì)發(fā)現(xiàn)根目錄的下的init文件是一個(gè)腳本,在該腳本的最后一行有這樣一段代碼:
?
………..
switchroot --movedev /sysroot
?
就是switchroot語(yǔ)句負(fù)責(zé)加載realfs,以及執(zhí)行realfs的init進(jìn)程。
對(duì)cpio-initrd的處理位于populate_rootfs函數(shù)中。
?
void __init populate_rootfs(void){
[1]? char *err = unpack_to_rootfs(__initramfs_start,
????????????????????????? ?__initramfs_end - __initramfs_start, 0);
[2]????? if (initrd_start) {
[3]????????????? err = unpack_to_rootfs((char *)initrd_start,
????????????????????????? initrd_end - initrd_start, 1);
????????
[4]????????????? if (!err) {
????????????????????????? printk(" it is\n");
????????????????????????? unpack_to_rootfs((char *)initrd_start,
?????????????????????????????????? initrd_end - initrd_start, 0);
????????????????????????? free_initrd_mem(initrd_start, initrd_end);
???????????????????????? return;
???????????????? }
[5]????????????? fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);
???????????????? if (fd >= 0) {
????????????????????????? sys_write(fd, (char *)initrd_start,
??????????????????????????????????????????? initrd_end - initrd_start);
????????????????????????? sys_close(fd);
????????????????????????? free_initrd_mem(initrd_start, initrd_end);
???????????????? }
}
?
代碼[1]:加載initramfs, initramfs位于地址__initramfs_start處,是內(nèi)核在編譯過(guò)程中生成的,initramfs的是作為內(nèi)核的一部分而存在的,不是 boot loader加載的。前面提到了現(xiàn)在initramfs沒有任何實(shí)質(zhì)內(nèi)容。
代碼[2]:判斷是否加載了initrd。無(wú)論哪種格式的initrd,都會(huì)被boot loader加載到地址initrd_start處。
代碼[3]:判斷加載的是不是cpio-initrd。實(shí)際上 unpack_to_rootfs有兩個(gè)功能一個(gè)是釋放cpio包,另一個(gè)就是判斷是不是cpio包, 這是通過(guò)最后一個(gè)參數(shù)來(lái)區(qū)分的, 0:釋放 1:查看。
代碼[4]:如果是cpio-initrd則將其內(nèi)容釋放出來(lái)到rootfs中。
代碼[5]:如果不是cpio-initrd,則認(rèn)為是一個(gè)image-initrd,將其內(nèi)容保存到/initrd.image中。在后面的image-initrd的處理代碼中會(huì)讀取/initrd.image。
對(duì)image-initrd的處理 在prepare_namespace函數(shù)里,包含了對(duì)image-initrd進(jìn)行處理的代碼,相關(guān)代碼如下:
?
void __init prepare_namespace(void){
[1]????? if (initrd_load())
???????????????? goto out;
out:
???????????????? umount_devfs("/dev");
[2]????????????? sys_mount(".", "/", NULL, MS_MOVE, NULL);
???????????????? sys_chroot(".");
???????????????? security_sb_post_mountroot();
???????????????? mount_devfs_fs ();
}
代碼[1]:執(zhí)行initrd_load函數(shù),將initrd載入,如果載入成功的話initrd_load函數(shù)會(huì)將realfs的根設(shè)置為當(dāng)前目錄。
代碼[2]:將當(dāng)前目錄即realfs的根mount為Linux VFS的根。initrd_load函數(shù)執(zhí)行完后,將真正的文件系統(tǒng)的根設(shè)置為當(dāng)前目錄。
initrd_load函數(shù)負(fù)責(zé)載入image-initrd,代碼如下:
?
int __init initrd_load(void)
{
[1]????? if (mount_initrd) {
???????????????? create_dev("/dev/ram", Root_RAM0, NULL);
[2]????????????? if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
????????????????????????? sys_unlink("/initrd.image");
????????????????????????? handle_initrd();
????????????????????????? return 1;
???????????????? }
???????? }
???????? sys_unlink("/initrd.image");
???????? return 0;
}
代碼[1]:如果加載initrd則建立一個(gè)ram0設(shè)備 /dev/ram。
代碼[2]:/initrd.image文件保存的就是image-initrd,rd_load_image函數(shù)執(zhí)行具體的加載操作,將 image-nitrd的文件內(nèi)容釋放到ram0里。判斷ROOT_DEV!=Root_RAM0的含義是,如果你在grub或者lilo里配置了 root=/dev/ram0 ,則實(shí)際上真正的根設(shè)備就是initrd了,所以就不把它作為initrd處理 ,而是作為realfs處理。
handle_initrd()函數(shù)負(fù)責(zé)對(duì)initrd進(jìn)行具體的處理,代碼如下:
?
???????? static void __init handle_initrd(void){
[1]????? real_root_dev = new_encode_dev(ROOT_DEV);
[2]????? create_dev("/dev/root.old", Root_RAM0, NULL);
???????? mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
[3]????? sys_mkdir("/old", 0700);
???????? root_fd = sys_open("/", 0, 0);
???????? old_fd = sys_open("/old", 0, 0);
??????? /* move initrd over / and chdir/chroot in initrd root */
[4]????? sys_chdir("/root");
???????? sys_mount(".", "/", NULL, MS_MOVE, NULL);
???????? sys_chroot(".");
???????? mount_devfs_fs ();
[5]????? pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
???????? if (pid > 0) {
???????????????? while (pid != sys_wait4(-1, &i, 0, NULL))
????????????????????????? yield();
???????? }
???????? /* move initrd to rootfs' /old */
???????? sys_fchdir(old_fd);
???????? sys_mount("/", ".", NULL, MS_MOVE, NULL);
???????? /* switch root and cwd back to / of rootfs */
[6]????? sys_fchdir(root_fd);
???????? sys_chroot(".");
???????? sys_close(old_fd);
???????? sys_close(root_fd);
???????? umount_devfs("/old/dev");
[7]????? if (new_decode_dev(real_root_dev) == Root_RAM0) {
???????????????? sys_chdir("/old");
???????????????? return;
???????? }
[8]????? ROOT_DEV = new_decode_dev(real_root_dev);
???????? mount_root();
[9]????? printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
???????? error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
???????? if (!error)
???????????????? printk("okay\n");
???????? else {
???????????????? int fd = sys_open("/dev/root.old", O_RDWR, 0);
???????????????? printk("failed\n");
???????????????? printk(KERN_NOTICE "Unmounting old root\n");
???????????????? sys_umount("/old", MNT_DETACH);
???????????????? printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
???????????????? if (fd < 0) {
????????????????????????? error = fd;
???????????????? } else {
????????????????????????? error = sys_ioctl(fd, BLKFLSBUF, 0);
????????????????????????? sys_close(fd);
???????????????? }
???????????????? printk(!error ? "okay\n" : "failed\n");
??????? }
????????
?
handle_initrd函數(shù)的主要功能是執(zhí)行initrd的linuxrc文件,并且將realfs的根目錄設(shè)置為當(dāng)前目錄。
代碼[1]:real_root_dev,是一個(gè)全局變量保存的是realfs的設(shè)備號(hào)。
代碼[2]:調(diào)用mount_block_root函數(shù)將initrd文件系統(tǒng)掛載到了VFS的/root下。
代碼[3]:提取rootfs的根的文件描述符并將其保存到root_fd。它的作用就是為了在chroot到initrd的文件系統(tǒng),處理完initrd之后要,還能夠返回rootfs。返回的代碼參考代碼[7]。
代碼[4]:chroot進(jìn)入initrd的文件系統(tǒng)。前面initrd已掛載到了rootfs的/root目錄。
代碼[5]:執(zhí)行initrd的linuxrc文件,等待其結(jié)束。
代碼[6]:initrd處理完之后,重新chroot進(jìn)入rootfs。
代碼[7]:如果real_root_dev在 linuxrc中重新設(shè)成Root_RAM0,則initrd就是最終的realfs了,改變當(dāng)前目錄到initrd中,不作后續(xù)處理直接返回。
代碼[8]:在linuxrc執(zhí)行完后,realfs設(shè)備已經(jīng)確定,調(diào)用mount_root函數(shù)將realfs掛載到root_fs的 /root目錄下,并將當(dāng)前目錄設(shè)置為/root。
代碼[9]:后面的代碼主要是做一些收尾的工作,將initrd的內(nèi)存盤釋放。
到此代碼分析完畢。
?
?
.結(jié)束語(yǔ)
通過(guò)本文前半部分對(duì)cpio-initrd和imag-initrd的闡述與對(duì)比以及后半部分的代碼分析,我相信讀者對(duì)Linux 2.6內(nèi)核的initrd技術(shù)有了一個(gè)較為全面的了解。在本文的最后,給出兩點(diǎn)最重要的結(jié)論:
1. 盡管Linux2.6既支持cpio-initrd,也支持image-initrd,但是cpio-initrd有著更大的優(yōu)勢(shì),在使用中我們應(yīng)該優(yōu)先考慮使用cpio格式的initrd。
2. cpio-initrd相對(duì)于image-initrd承擔(dān)了更多的初始化責(zé)任,這種變化也可以看作是內(nèi)核代碼的用戶層化的一種體現(xiàn),我們?cè)谄渌闹T如 FUSE等項(xiàng)目中也看到了將內(nèi)核功能擴(kuò)展到用戶層實(shí)現(xiàn)的嘗試。精簡(jiǎn)內(nèi)核代碼,將部分功能移植到用戶層必然是linux內(nèi)核發(fā)展的一個(gè)趨勢(shì)。
從下面三篇文章中,可以獲得更多的關(guān)于initramfs的知識(shí):
[1]http://tree.celinuxforum.org/pubwiki/moin.cgi/EarlyUserSpace
[2]http://lwn.net/Articles/14776/
[3]http://www.ussg.iu.edu/hypermail/linux/kernel/0211.0/0341.html
從下面這篇文章中讀者可以了解到關(guān)于linux VSF、rootfs的相關(guān)知識(shí):
[4] http://www.ibm.com/developerworks/cn/linux/l-vfs/
下面是一些initrd的參考資料:
[5] http://www.die.net/doc/linux/man/man4/initrd.4.html
[6] http://www.gd-linux.org/bbs/archive/index.php/t-1661.html
評(píng)論
查看更多