Linux 的啟動流程目前比較流行的方式主要是以下步驟:
1、引導器(例如 GRUB)啟動;
2、內核啟動;
3、系統進程啟動與配置。
本文以 GRUB 為研究對象,對 GRUB 啟動與內核啟動兩個部分進行描述,關于系統進程的進一步啟動與配置將用另一篇文章來說明。
常見的目錄結構 (以 CentOS 5.3 為例):
/boot
|-- System.map-2.6.18-128.el5
|-- System.map-2.6.18-128.el5xen
|-- config-2.6.18-128.el5
|-- config-2.6.18-128.el5xen
|-- initrd-2.6.18-128.el5.img
|-- initrd-2.6.18-128.el5xen.img
|-- lost+found
|-- memtest86+-1.65
|-- message
|-- symvers-2.6.18-128.el5.gz
|-- symvers-2.6.18-128.el5xen.gz
|-- vmlinuz-2.6.18-128.el5
|-- vmlinuz-2.6.18-128.el5xen
|-- xen-syms-2.6.18-128.el5
|-- xen.gz-2.6.18-128.el5
`-- grub
|-- device.map
|-- e2fs_stage1_5
|-- fat_stage1_5
|-- ffs_stage1_5
|-- grub.conf
|-- iso9660_stage1_5
|-- jfs_stage1_5
|-- menu.lst -> ./grub.conf
|-
- minix_stage1_5
|-- reiserfs_stage1_5
|-- splash.xpm.gz
|-- stage1
|-- stage2
|-- ufs2_stage1_5
|-- vstafs_stage1_5
`-- xfs_stage1_5
圖一: CentOS 5.3 的 /boot 目錄
目錄分作兩大部分,一個是 /boot 目錄下除 grub 目錄以外的所有文件,這些是 Linux 的內核以及內核啟動相關的一些文件;另一個就是 grub 下的所有文件, GRUB 引導器啟動所需要的所有文件都在 grub 目錄下。
核心文件組成:
/boot
|-- System.map-2.6.18-128.el5
|-- initrd-2.6.18-128.el5.img
|-- vmlinuz-2.6.18-128.el5
`-- grub
|-- grub.conf
|-- menu.lst -> ./grub.conf
|-- stage1
|-- stage2
圖二: 核心文件說明
圖二是一個簡化了的目錄結構,在大多數Linux實現中,這些是 Linux 啟動最核心的幾個文件,下面我們對這個結構中的文件加以說明。
內核文件說明:
Linux 內核啟動相關文件:
vmlinuz --- Linux 內核
這個就是 Linux 可引導的、壓縮的內核文件。
此文件通常后面帶有內核版本號,但不是必需的,但有利于系統中存在多個不同的內核時加以區分。
vmlinuz 通常在 2.4 版內核時有兩種方法建立,一個是通過編譯時使用 make zimage 命令,這個方法是老的 2.4 版內核所支持的方式,用于生成較小的內核(512K 以下), 同時,在 2.4 版內核編譯時使用 make bzimage 則可以生成一個大內核,其中 bz 的意思是 big zimage。 二者的主要區別在于小內核(zimage 形勢)可以很小(4K),放在磁盤的前八個引導扇區,在裝載時,先裝載到
0x1000:0000 高端內存,然后再移動到 0x0200:0000 位置的低端內存,然后啟動CPU,所以可以不需要單獨的引導器; 而 bzimage 形式較大,在 512K 以上
,只能加載到
0x1000:0000 高端內存,然后啟動CPU,需要有獨立的引導器進行引導。
vmlinuz 在 2.6 版內核中并不直接支持 zimage 小內核方式,2.6版內核的編譯過程不需要 make zimage 或 make bzimage 這一步 。
vmlinuz 雖然采用了 gzip 進行壓縮 , 但由于其頭部中添加了精簡過的代碼,所以不能直接用 gunzip 直接解壓,我們通過觀察一般的 gzip縮文件,發現有一個特征碼 "1f8b" 。于是分析 vmlinuz 這個文件:
# xxd vmlinuz-2.6.18-128.el5 |egrep "/b1f8b" |head -n5
0002080: 1b00
1f8b
0800 e642 7749 0203 ec3b 6d78 .......BwI...;mx
00315f0: ebe4 177f 065d 6e19 434a 4a5a 1f8b 111e .....]n.CJJZ....
0058390: d1ad 1e11 0683 9f1f a857 1f8b a542 cb27 .........W...B.'
0073cb0: 9b51 e43c 482b 3685 1f8b 05f5 32fd b758 .Q.
0086a50: ed06 0bb5 1f8b 6d67 930c d7d0 2c6c 6c18 ......mg....,ll.
發現該特征碼在 0x2082 處,由以下命令可以摘除這個部分:
# dd if=
vmlinuz-2.6.18-128.el5 of=vmlinuztest.gz bs=1 skip=$((0x2082))
然后解壓 vmlinuz
test.gz 即可得到二進制的內核。
值得一提的是,2.4 以后版本的內核,不再提供無需引導器引導的機制
。
initrd --- initialized ram disk 初始化 RAM 磁盤
啟動器(boot loader 即本文的 GRUB)會在內核啟動前把 initrd 裝入內存,該文件的作用是生成一個 RAM 磁盤,并在其上形成根文件系統,內核啟動時會在訪問真實的磁盤根文件系統前訪問這個 RAM 磁盤中的根文件系統。2.6 版內核中,當內核執行完 initrd 中的內容后,對于 cpio 類型的 initrd ,會由 initrd 負責執行switchroot 來切換根文析系統到真實的文件系統中去,并開始真正的 init 進程;對于 image-initrd 這個類型來說,內核執行完 initrd 這個階段以后,會返回到內核,繼續內核初始化,然后由內核去調用真實文件系統中的 init 。本文的主要內容是介紹 Linux 2.6 版引導的過程,對于initrd 的更多細節不再贅述,有興趣的讀者,可以查閱
相關的鏈接
。 關于 initrd 的動手實驗,則可以在
本站中找到
。
此文件通常后面帶有內核版本號,但不是必需的,但有利于系統中存在多個不同的內核時加以區分。
關于 initrd 的另一個重點,是為什么要使用它,initrd 最重要的作用在于使引導過程更加靈活。為了在各種硬件平臺上啟動,將所有的硬件驅動都放到內核中顯然不現實,initrd 的作用之一就是加載硬件驅動模塊,從而可以在內核中只包含最基本的硬件驅動即可,將加載不同硬件驅動的任務交給 initrd ;關于initrd 的另一個作用是支持 usb 啟動,由于 usb 從驅動加載到真正可用的過程較慢,可能需要幾秒鐘的時間比較慢,在內核訪問usb時,USB 設備可能還沒初始化完成,將該過程放入到 initrd 中可以進行延時,完成正確加載和引導。
在某些情況下,我們可以使用 noinitrd 參數,使啟動過程不使用 initrd 文件是可能的。
System.map --- 內核符號映射表
在弄清楚 System.map 的作用以前,首先要先了解兩個名詞,其中一個叫做 symbol(符號),另一個叫做 Oops 。
Symbol : 符號,學過程序設計的話應該知道,一個符號是一個程序的創建塊,它是一個變量名或一個函數名。 這里為什么要提到這個名詞呢? 因為 Linux 內核并不使用符號來調用函數,而是直接使用函數的地址(指針), 這似乎造成了一個矛盾,因為編程的人并不喜歡使用地址的方式,于是符號表產生了,它允許在編程的過程中,使用符號,但是在編譯時使用地址,”符號映射表 “由此產生了,它就是 System.map .
用 more 命令查看 System.map 文件可以看到類似下面的段:
64位的系統:
ffffffff81000000 A _text
ffffffff81000000 T startup_64
ffffffff810000b7 t ident_complete
ffffffff81000100 T secondary_startup_64
32位的系統:
c0100000 A _text
c0100000 T startup_32
c01000c6 t checkCPUtype
c0100147 t is486
Oops : 當內核引用了一個無效指針時,通常被稱為 Oops ,說明內核存在一個Bug。 內核在出現此錯誤時,會由 klogd 這個服務將此錯誤記載到日志中,如果該日志指出一個地址錯誤,顯然還需要我們花些時間來找該地址對應的符號,klogd 通過檢視 System.map 直接將符號取出,并記載該符號引發了一個 Oops 。
通過以上的描述,我們得到一個結論:System.map 是一個靜態的內核符號映射表!
既然說到靜態,那么就說明如果是在運行期間動態加載的某些模塊,可能不在其中,如何得到它們呢? 系統中的 proc 文件系統中存在一個動態的映射表, 在 2.6 Kernel 中通常是 /proc/kallsyms 。
另外,類似 lsof 和 ps 等命令,是需要這個映射表的存在的,但既使沒有這個文件,系統的啟動依然可以進行。
GRUB 文件說明:
stage1 --- 磁盤引導第一階段
當 BIOS 加電自檢完成以后,假設系統是從硬盤啟動,則 BIOS 的最后一件事情就是讀取該硬盤的 0 道 0 面 1 扇區,即我們常用說的 MBR ,它只有 512 個字節大小,它與 stage1 具有什么樣的關系呢? 我們先來看一下關于 MBR 的構成:
圖三:MBR 的組成(摘自 IBM 官方網站)
MBR 共由三個部分組成:1、Bootloader 就是引導代碼,其作用主要是加載第二階段啟動(即stage2),2、Partition table 分區表, 3、Magic Number 魔數,就是那個 55AA 標志,用以檢驗該 MBR 的有效性。
我們可以用如下命令導出該扇區到文件(假設該硬盤為 hda):
dd if=/dev/hda of=mbr.bin bs=512 count=1
然后我們用
xxd mbr.bin
xxd /boot/grub/stage1
仔細對比發現,該扇區的 Bootloader 部分與 MagicNumber部分與 stage1 文件完全一樣,原來,在使用 grub 的安裝命令進行引導器安裝時,grub 會用 stage1 文件的前 446 字節覆蓋 MBR的前446 字節。在這里我們得到另一個啟示,那就是不能簡單的認為 MBR 等價于 stage1 ,為了保護 MBR ,我們還是應該對 MBR 做備份。
stage1_5 --- 關于文件系統格式的“魔術師”
應該注意到,所有帶有 stage1_5 字樣的文件,全部都和文件系統名字有關:
|-- e2fs_stage1_5
|-- fat_stage1_5
|-- ffs_stage1_5
|-- iso9660_stage1_5
|-- jfs_stage1_5
|-
- minix_stage1_5
|-- reiserfs_stage1_5
|-- ufs2_stage1_5
|-- vstafs_stage1_5
`-- xfs_stage1_5
顧名思義,其實 stage1_5d 確實是在 stage1 和 stage2 之間運行的,它包括了一些常見的文件系統的識別能力,這些文件的存在,意味著 grub 可以從多種文件系統中讀取并加載 Linux 內核。
這個特性使得 grub 更加靈活的處理不同的文件系統格式。
雖然有
e2fs_stage1_5 這個文件的存在,但經過實驗,我們發現在默認情況下編譯的內核如果裝載在 ext3 文件系統上,是不需要 stage1_5這個過程的。
stage2 --- 真正的 grub 就在這里
注意到 stage2 的文件尺寸,超過了 512 字節的大小,所以 grub 本身并不能放到 mbr 中去,mbr 那446字節的作用,主要就是找到這個 stage2,它讀取 menu.list 文件顯示系統引導菜單,識別不同的引導指令,并完成 vmlinuz 和 initrd 的加載,其實,這個就是真正的 grub 了!
menu.list 文件就是 grub 的引導菜單, grub 會執行菜單里的命令,完成引導,因為紅帽這個系列的系統進行了一些特別的設置,所以存在一個叫做 grub.conf 的軟鏈接,指向 menu.list 。
結論:
啟動中最重要的東西:mbr/stage1 、grub/stage2 、kernel/vmlinuz ,三者是最最核心的。stage1_5 的作用是使 grub 有處理更多文件系統的靈活能力,而 initrd 使內核啟動更加靈活,在不同的平臺上,對不同的硬件,使用不同的驅動,從而減小靜態內核的尺寸。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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