Linux 2.6 內(nèi)核引導(dǎo)過程分析
?
上一篇文章介紹了Linux內(nèi)核的編譯過程,接下來本文利用生成的內(nèi)核,介紹Linux的內(nèi)核的引導(dǎo)過程。
本文以x86為例,x86與嵌入式系統(tǒng)的區(qū)別在于多了一個(gè)BIOS轉(zhuǎn)移到BootLoader的過程。
Linux內(nèi)核引導(dǎo)的過程包含多個(gè)階段,接下來將依次解讀:
1. 系統(tǒng)加電階段 1:
系統(tǒng)加電時(shí),處理器會(huì)執(zhí)行一個(gè)位于已知位置處的代碼。PC中即我們熟知的BIOS,它保存在主板的閃存中。
BIOS從0xFFFF0處開始執(zhí)行,首先執(zhí)行POST(加電自檢),檢查系統(tǒng)必備的引導(dǎo)設(shè)備是否存在,如內(nèi)存/磁盤等硬件設(shè)備。然后BIOS進(jìn)行本地設(shè)備的枚舉和初始化。BIOS由兩部分組成:POST代碼和運(yùn)行時(shí)服務(wù),POST完成后會(huì)被清出內(nèi)存,而運(yùn)行時(shí)服務(wù)會(huì)駐留于內(nèi)存供目標(biāo)操作系統(tǒng)調(diào)用服務(wù)。
?
然后BIOS讀取CMOS中的引導(dǎo)設(shè)備信息來搜索處于活動(dòng)狀態(tài)的可引導(dǎo)設(shè)備(可以是軟盤,CD-ROM,硬盤上某一分區(qū)等),從引導(dǎo)設(shè)備中讀取第一個(gè)扇區(qū),該扇區(qū)包含著引導(dǎo)程序。由上圖可知引導(dǎo)包含兩個(gè)階段。
當(dāng)找到第一個(gè)引導(dǎo)設(shè)備后,第一階段的引導(dǎo)程序就會(huì)被裝載入RAM并執(zhí)行,該引導(dǎo)程序大小一般小于一個(gè)扇區(qū),其作用是加載第二階段的引導(dǎo)加載程序,MBR就是熟知的第一階段引導(dǎo)加載器。通常,Linux都是從硬盤上引導(dǎo)的,其中主引導(dǎo)目錄(MBR)中包含主引導(dǎo)加載程序。MBR 是一個(gè) 512 字節(jié)大小的扇區(qū),位于磁盤上的第一個(gè)扇區(qū)中(0 道 0 柱面1 扇區(qū))。當(dāng) MBR 被加載到 RAM 中之后,BIOS 就會(huì)將控制權(quán)交給 MBR。
當(dāng)?shù)诙A段的引導(dǎo)加載程序被裝入 RAM 并執(zhí)行時(shí),通常會(huì)顯示一個(gè)動(dòng)畫屏幕,并將 Linux 和一個(gè)可選的初始 RAM磁盤(臨時(shí)根文件系統(tǒng))加載到內(nèi)存中。在加載映像時(shí),第二階段的引導(dǎo)加載程序就會(huì)將控制權(quán)交給內(nèi)核映像,然后內(nèi)核就可以進(jìn)行解壓和初始化了。在這個(gè)階段 中,第二階段的引導(dǎo)加載程序會(huì)檢測系統(tǒng)硬件、枚舉系統(tǒng)鏈接的硬件設(shè)備、掛載根設(shè)備,然后加載必要的內(nèi)核模塊。
在引導(dǎo)程序GRUB被BIOS裝入到內(nèi)存并獲得控制權(quán)后,讀取/boot/grub.conf中的引導(dǎo)列表選擇默認(rèn)要啟動(dòng)的內(nèi)核鏡像bzImage。根據(jù)bzImage中前512byte的bootsect中的setup,vmLinux.bin所占扇區(qū)的大小,以及內(nèi)核是否為大內(nèi)核鏡像將bootsect->0x00090000, setup->0x00090020, vmLinux.bin -> 0x00100000(big) 0x00010000(else)。
將內(nèi)核搬到相應(yīng)的地址后,引導(dǎo)程序跳轉(zhuǎn)到物理地址0x00090020,即setup所在的物理地址,開始執(zhí)行setup代碼。
接下來會(huì)詳細(xì)介紹下兩階段的引導(dǎo)過程及有關(guān)GRUB的知識(shí)。
1) 第一階段引導(dǎo)加載程序
MBR 中的主引導(dǎo)加載程序是一個(gè) 512 字節(jié)大小的映像,其中包含程序代碼和一個(gè)小分區(qū)表(參見圖 2)。前446個(gè)字節(jié)是主引導(dǎo)加載程序,其中包含可執(zhí)行代碼和錯(cuò)誤消息文本。接下來的 64 個(gè)字節(jié)是分區(qū)表,其中包含 4 個(gè)分區(qū)的記錄(每個(gè)記錄的大小是 16 個(gè)字節(jié))。MBR 以兩個(gè)特殊數(shù)字的字節(jié)(0xAA55)結(jié)束。這個(gè)數(shù)字會(huì)用來進(jìn)行MBR的有效性檢查。
?主引導(dǎo)加載程序的工作是查找并加載次引導(dǎo)加載程序(第二階段)。它是通過在分區(qū)表中查找一個(gè)活動(dòng)分區(qū)來實(shí)現(xiàn)這種功能的。當(dāng)找到一個(gè)活動(dòng)分區(qū)時(shí),它會(huì)掃描分 區(qū)表中的其他分區(qū),以確保它們都不是活動(dòng)的。當(dāng)這個(gè)過程驗(yàn)證完成之后,就將活動(dòng)分區(qū)的引導(dǎo)記錄從這個(gè)設(shè)備中讀入 RAM 中并執(zhí)行它。
2) 第二階段引導(dǎo)加載程序
次引導(dǎo)加載程序(第二階段引導(dǎo)加載程序)可以更形象地稱為內(nèi)核加載程序。這個(gè)階段的任務(wù)是加載 Linux內(nèi)核和可選的初始 RAM 磁盤。
GRUB有關(guān)的信息:
在 x86 PC 環(huán)境中,第一階段和第二階段的引導(dǎo)加載程序一起稱為 Linux Loader(LILO)或 GRand Unified Bootloader(GRUB)。由于 LILO 有一些缺點(diǎn),而 GRUB 克服了這些缺點(diǎn),因此下面讓我們就來看一下 GRUB。
關(guān)于 GRUB,很好的一件事情是它包含了有關(guān) Linux 文件系統(tǒng)的知識(shí)。GRUB 不像 LILO 一樣使用裸扇區(qū),而是可以從 ext2或 ext3 文件系統(tǒng)中加載 Linux 內(nèi)核。它是通過將兩階段的引導(dǎo)加載程序轉(zhuǎn)換成三階段的引導(dǎo)加載程序來實(shí)現(xiàn)這項(xiàng)功能的。階段 1 (MBR)引導(dǎo)了一個(gè)階段 1.5 的引導(dǎo)加載程序,它可以理解包含 Linux 內(nèi)核映像的特殊文件系統(tǒng)。這方面的例子包括reiserfs_stage1_5(要從 Reiser 日志文件系統(tǒng)上進(jìn)行加載)或 e2fs_stage1_5(要從 ext2 或 ext3 文件系統(tǒng)上進(jìn)行加載)。當(dāng)階段 1.5 的引導(dǎo)加載程序被加載并運(yùn)行時(shí),階段 2 的引導(dǎo)加載程序就可以進(jìn)行加載了。當(dāng)階段 2 加載之后,GRUB 就可以在請(qǐng)求時(shí)顯示可用內(nèi)核列表(在 /etc/grub.conf 中進(jìn)行定義,同時(shí)還有幾個(gè)軟符號(hào)鏈接/etc/grub/menu.lst 和 /etc/grub.conf)。我們可以選擇內(nèi)核甚至修改附加內(nèi)核參數(shù)。另外,我們也可以使用一個(gè)命令行的shell 對(duì)引導(dǎo)過程進(jìn)行高級(jí)手工控制。將第二階段的引導(dǎo)加載程序加載到內(nèi)存中之后,就可以對(duì)文件系統(tǒng)進(jìn)行查詢了,并將默認(rèn)的內(nèi)核映像和 initrd 映像加載到內(nèi)存中。當(dāng)這些映像文件準(zhǔn)備好之后,階段 2 的引導(dǎo)加載程序就可以調(diào)用內(nèi)核映像了。
?
2. 階段1 ~ 階段2:
該階段由文件seup.S生成的代碼塊進(jìn)行系統(tǒng)設(shè)備的探索,將探測到的相關(guān)信息保存到內(nèi)存地址中,在系統(tǒng)最終初始化時(shí)由系統(tǒng)使用。探測完成后跳轉(zhuǎn)到物理地址0x00100000,開始執(zhí)行由/src/arch/i386/compressed/head.S生成的代碼塊。
?
3. 階段2 ~ 階段3:
該階段由head.S生成的代碼塊執(zhí)行,0x00100000處及startup_32處,該匯編代碼為調(diào)用C語言函數(shù)設(shè)置相應(yīng)的參數(shù)及棧指針后調(diào)用C函數(shù)decompress_kernel對(duì)壓縮的內(nèi)核進(jìn)行解壓縮,并將解壓的內(nèi)核片段放置到兩個(gè)不同物理內(nèi)存區(qū)域中,然后head.s中復(fù)制move,搬移的代碼段移動(dòng)到物理地址0x00010000處。然后跳轉(zhuǎn)到該地址,執(zhí)行該代碼塊。
?
4. 階段3 ~ 階段4:
該階段執(zhí)行復(fù)制到物理地址0x00010000的搬遷代碼,該部分的搬遷代碼將位于兩個(gè)物理區(qū)域的解壓縮內(nèi)核代碼片段移動(dòng)到物理地址0x00100000.
?
5. 階段4 ~ 階段5:
執(zhí)行完move后,跳到0x00100000,執(zhí)行src/arch/i386/kernel/head.S中的startup_32的代碼段,注意與上面startup_32的不同,開始執(zhí)行系統(tǒng)核心的初始化。
?
6. 階段5 ~ 系統(tǒng)完全啟動(dòng):
在startup_32函數(shù)(也就是系統(tǒng)初始化進(jìn)程init_task,也叫0號(hào)進(jìn)程,idle進(jìn)程)中。該進(jìn)程完成了系統(tǒng)內(nèi)存管理子系統(tǒng)/進(jìn)程管理子系統(tǒng),中斷異常子系統(tǒng),時(shí)間度量子系統(tǒng)等初始化工作.會(huì)對(duì)頁表進(jìn)行初始化,并啟用內(nèi)存分頁功能,為FPU檢測CPU類型,調(diào)用start_kernel函數(shù),執(zhí)行與體系結(jié)構(gòu)無關(guān)的Linux內(nèi)核部分.
通過調(diào)用 start_kernel,會(huì)調(diào)用一系列初始化函數(shù)來設(shè)置中斷,執(zhí)行進(jìn)一步的內(nèi)存配置,并加載初始 RAM 磁盤。
注:在內(nèi)核引導(dǎo)過程中,初始 RAM 磁盤(initrd)是由階段 2 引導(dǎo)加載程序加載到內(nèi)存中的,它會(huì)被復(fù)制到RAM中并掛載到系統(tǒng)上。這個(gè)initrd會(huì)作為RAM中的臨時(shí)根文件系統(tǒng)使用,并允許內(nèi)核在沒有掛載任何物理磁盤的情況下完整地實(shí)現(xiàn)引導(dǎo)。由于與外圍設(shè)備進(jìn)行交互所需要的模塊可能是initrd的一部分,因此內(nèi)核可以非常小,但是仍然需要支持大量可能的硬件配置。在內(nèi)核引導(dǎo)之后,就可以正式裝備根文件系統(tǒng)了(通過 pivot_root):此時(shí)會(huì)將 initrd 根文件系統(tǒng)卸載掉,并掛載真正的根文件系統(tǒng)。initrd 函數(shù)讓我們可以創(chuàng)建一個(gè)小型的 Linux內(nèi)核,其中包括作為可加載模塊編譯的驅(qū)動(dòng)程序。這些可加載的模塊為內(nèi)核提供了訪問磁盤和磁盤上的文件系統(tǒng)的方法,并為其他硬件提供了驅(qū)動(dòng)程序。由于根文件系統(tǒng)是磁盤上的一個(gè)文件系統(tǒng),因此 initrd函數(shù)會(huì)提供一種啟動(dòng)方法來獲得對(duì)磁盤的訪問,并掛載真正的根文件系統(tǒng)。在一個(gè)沒有硬盤的嵌入式環(huán)境中,initrd 可以是最終的根文件系統(tǒng),或者也可以通過網(wǎng)絡(luò)文件系統(tǒng)(NFS)來掛載最終的根文件系統(tǒng)。
最后調(diào)用kernel_thread()創(chuàng)建一個(gè)名為init的內(nèi)核線程,然后0號(hào)進(jìn)程調(diào)用調(diào)度器schedule(),釋放了處理器的使用權(quán),成為了系統(tǒng)的idle進(jìn)程。
在內(nèi)核線程init獲得處理器后,首先完成對(duì)稱多處理器系統(tǒng)中應(yīng)用處理器的初始化;然后掛載系統(tǒng)的根文件系統(tǒng),完成系統(tǒng)總線/網(wǎng)絡(luò)協(xié)議棧等的初始化;最后通過調(diào)用execve()開始執(zhí)行用戶態(tài)程序/sbin/init,此時(shí)內(nèi)核線程init轉(zhuǎn)換成了用戶進(jìn)程。/sbin/init是系統(tǒng)中所有進(jìn)程(除idle進(jìn)程)的祖先,它首先讀取系統(tǒng)的配置文件/etc/inittab,根據(jù)該配置文件完成系統(tǒng)的最終初始化.
?
這篇文章也是綜合了我看到的多個(gè)資料,每個(gè)資料都或多或少的有些遺漏,所以綜合起來,希望能全面。
接下來,我將介紹些有關(guān)kernel其他方面的知識(shí)。
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
