Linux啟動深度分析,5000字乾貨筆記(建議收藏)

全國Linux領域貢獻最多的十大工程師之一

X86架構服務器資深專家,30年IT領域經驗

文章摘自聯想超級課


今天我們講一下X86Linux的啟動。

一個計算機的啟動,最主要就是CPU的啟動。CPU啟動就是把這個CPU的裡邊的各個寄存器設置到一個已知的、固定的狀態,並且從這裡開始執行。

01 指針啟動

如下圖的右邊這有幾個就是 X86 的話的一些比較清晰可見的寄存器,其中最重要的是 EIP,即指令指針。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)


指令指針就表示它要執行的下一個指令,當我們 CPU 一發生 Reset 以後,這個EIP作為一個固定的值,就會從這裡開始執行,這裡通常是 BIOS 的程序。

X86 的 CPU 有三種啟動方式:

  • 第一種是加電啟動
  • 第二種是 Reset
  • 第三個是 INIT

加電啟動和 Reset 非常類似,不但把右邊的這些值都設到一個固定的狀態,我們還把CPU其它的很多寄存器都設置到一個已知的固定的狀態。

那麼INIT不同呢?它僅僅是把右邊的這些設置到一個固定的已知的狀態,其它的寄存器它基本上保持不動。目的就是為了你啟動完之後還能讀原來寄存器裡的東西。這些寄存器主要是記錄了一些上一次啟動的CPU錯誤狀態,這樣我們可以以此來做診斷。

現在說Power On 通常叫做冷啟動,Reset 我們通常叫做熱啟動,它倆是基本一致,但是 INIT 就很不同,但是總體上來說,它還是從CPU一個固定的狀態開始執行程序,X86 Reset之後,它就處於一個 Real Address 模式。

這個講起來話長,因為 X86 的歷史比較悠久,從80、86、80、88 ,它現在有Real Address模式有protect模式,還有現在X86 64模式,Reset 之後它一定是處於 Real Address 模式,每個CPU被Reset之後,它從一個固定的地方開始執行。

第一個通常是它要執行一個協議,選取一個CPU來執行,其他CPU都等待,我們要選取一個所謂主CPU(其實它的標準術語叫Bootstrapping CPU),就是說選完了之後,只有主CPU 繼續執行,其它都進入一個等待狀態,就是等待所有的其它CPU都等待這個BSP給它發指令,它再開始繼續執行。

BSP選出來之後,它就開始執行BIOS代碼,這個BIOS代碼通常我們又叫它POST過程,它相當於機器一個自檢,當機器自檢完了 ,BIOS走完了之後,它要做的一件事情就是要找到一個文件,並且把它讀到內存裡,然後開始執行它。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)

這個時候我們認為post自檢完了,要開始讀取並執行第一個外部程序,一般叫它Boot Load,那麼它這個如何找到並讀取並執行它呢?

第一個問題主要是找到並讀取它,這裡又分幾種,比如

  • 從光盤啟動
  • 從網絡啟動
  • Legacy BIOS模式啟動
  • UEFI模式啟動

Legacy BIOS 啟動

Legacy BIOS一般都從某個硬盤上啟動,通常在過去這個叫C盤,因為過去只有一個盤叫C盤,現在當然磁盤很多了,你還是要指定一個啟動盤,從哪個卡上的哪個盤來啟動。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)


如果是指定了,它就讀你指定的盤,這個硬盤的同一個扇區,512字節。讀進來之後,首先檢查扇區的標記,標記這塊盤是不是可以啟動的,如果標記它不是可以啟動的,那麼它會報告一個錯誤,大概就是找不到啟動盤之類。

如果標記的它是可以啟動的一個硬盤,那麼它就會開始執行,當然了這時候這個扇區的內容已經在內存裡了,它就開始執行從0開始一個446字節長度的這個小程序,當然了我們看這個程序很小,所以它其實它還要想辦法,讀到另外一個更大的程序,才能夠把我們說的Boot Load,比如說Grub完完全全加載起來,光446字節肯定是不行的。

UEFI BIOS 啟動

UEFI BIOS會好一點,它主板上記錄的是一個啟動文件路徑,這個啟動文件路徑就是某個盤某個分區裡面的某個文件,這個分區是什麼文件系統呢?通常它是FAT32文件系統,又叫ESP文件系統。

如果UEFI想啟動,一定存在這麼一個分區,它有這麼一個FAT文件系統,然後在裡面有某一個程序,都是寫在NVRAM裡頭,我們叫做啟動項,它把那個程序讀進來,執行它。

這樣看UEFI肯定比Legacy要好多了。Legacy第一步你必須是446字節的那個,UEFI一下子可以把整個文件讀進來,不需要分幾步來做。

光盤啟動

從光盤啟動是怎麼啟動呢?光盤在Legacy模式下,它有一個讀一個特殊的記錄,這個記錄標註它是啟動記錄,這個啟動記錄可以任意的,沒有過去光盤446字節的限制了。

所以說其實儘管在Legacy模式下,從CD上啟動它也要容易一些,因為它就是讀固定的記錄,記錄一個任意大的文件,讀進來直接執行。

如果在UEFI下它也是一個啟動記錄,但是這個記錄本身,並不是一個文件,它是一個文件系統的鏡像。這個文件系統有啥東西呢?可能主要有啟動文件。

為什麼UEFI跟Legacy啟動模式的不一樣呢,在理論上來說它需要區分,你是Legacy BIOS,你就讀那一個記錄,那個記錄就是個文件,直接讀取來執行它。那如果你是UEFI,那你就找到FAT32的鏡像,這裡面有文件,然後你把它讀進來執行。

不管是Legacy還是UEFI BIOS,如果都不行,還可以從網絡啟動,從網絡啟動也是要首先要找到並且把一個啟動文件讀進來,那麼這裡它就稍微費勁一點。

網絡啟動

網絡啟動最早功能全部寫在網卡里,也就是說每個網卡廠商都自己寫了一段從網絡的啟動的程序,然後把它燒在網卡里,後來有UEFI之後,Intel就主導制定了一個PXE協議,這個PXE其實就是把這些網絡啟動要用的公共的東西,納入到了BIOS功能裡。

比如說TCP-IP、TFTP、DHCP,這些東西跟網卡沒有關係,網卡要做的就是收發報而已。

Linux啟動深度分析,5000字乾貨筆記(建議收藏)

PXE協議分四步:

第一步,必須有DHCP,先發DHCP包,我要取得IP地址,才能做後面的事情,如果你是想啟動的話,在DHCP包裡面還附加了很多東西,附加了一些標記。

第二步,在包發出去之後,看到這個標記的人,如果他能提供PXE服務,也會響應關於PXE的東西。

PXE響應它什麼呢?有幾個IP地址,這幾個地址是可以提供啟動服務的,而且每個IP地址它什麼文件,都可以告訴你。

最後一步,有了啟動服務器的地址和我要啟動的文件,那麼我就通過TFTP從這個服務器下載這個文件就可以了。

下面這是一檔例子,在標準的DHCP服務裡,附帶提供PXE支持,它支持在哪呢:首先它檢視DHCP包裡面是不是包含這個字段,如果包含這個字段,就告訴你啟動服務器是誰,你去跟他找啟動文件。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)

光告訴你啟動服務器是誰還不行,還必須告訴你啟動文件是啥,啟動文件根據機型不同它是有區別的,比如你是啟動Power PC,還要啟動mips,還要啟動SUN的SPARC。

02 啟動Linux需要什麼

光是X86就有三種架構

,一種是64位的UEFI,一種是32位的UEFI,另外一種就是老的i386的PC模式,叫做Legacy BIOS模式。

所以根據包裡面告訴我的架構不同,就反饋你文件名,不同的文件名給你,等於交互完之後就得到兩個:一個是不同IP地址,一個是文件名。通過TFTP給它下載下來開始執行就好了。後面的就跟從硬盤上是一樣的了。

如果是啟動Linux呢?Linux現在需要什麼東西?

Linux 只需要兩個東西(當然了其實它是需要三個東西),但文件只需要兩個:一個是內核文件,一個叫做INITRAMFS文件。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)

我們都知道內核文件肯定需要,任何一個操作系統起來都必須先把內核設置好,啟動好。

INITRAMFS是一個給Linux用作根的一個文件,大家知道Unix啟動一定需要有一個根文件系統,這個Unix才能夠運行。如果在內核啟動的時候,到最後找不到這個根文件系統就會啟動失敗。

有了INITRAMFS文件之後,Linux內核的啟動過程中會展開INITRAMFS文件。這個文件其實就是一個CPIO包,它把這個包展開到它的內存裡,在展開的內存裡會看到目錄架構,會有文件,它就到這一塊區域當作它的根。

也就是說無論如何,內核文件+INITRAMFS文件一定能夠啟動一個小Linux。

什麼叫小Linux,因為INITRAMFS不可能很大,一般幾十兆,所以它啟動起來之後,它就是一個完完全全放在內存裡的Linux,它小是因為這個文件大小受到了限制,那麼內核文件加上INITRAMFS展開後,把它作為一個根文件系統,這就是一個小的全部在內存裡的Linux。

麻雀雖小,五臟俱全,有內存和輸入輸出,就可以做任何事情,所以它適合做一些工具類的東西,安裝肯定是也通過它就可以繼續安裝,比如往硬盤上裝東西或者做外設的配置更改、FW升級等,它非常適合做一次性的工作。

我們通常用一個Live Image來啟動這樣一個Linux,當然它稍微比這個還複雜一點,但是基本上還是一個完完全全在內存裡的小Linux,啟動完後把U盤拿走也沒有任何問題。

當然我們大部分的情況下啟動可能不僅僅是想啟動一個內核裡的小Linux。

比如當前我有一個硬盤,硬盤上放了一個很大的Linux,幾十個G,想啟動這個系統就根據啟動Grub的參數,叫root=什麼什麼,這時候這個小Linux就會找這個root,root找到了之後(通常它是一個硬盤上的一個分區的一個文件系統),我們給mount到一個目錄下面,因為當前已有根,只不過這個根完完全全是個內存文件系統,我們可能會創建一個目錄叫newroot,然後把你制定的這個根mount到這個newroot下面。

mount好後把其它的進程都終止掉,然後把當前這個根裡面的基本上所有其它文件都刪掉釋放內存。但newroot必須保留,因為Newroot掛載的新的根文件系統,到最後就執行一個操作叫switch_root,把這個newroot變為根再執行根裡面的INIT,就完成拉起來了一個新的Linux,即硬盤上的Linux。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)

在這裡內核啟動的時候已經固定了,也就是說我們拉起來的這個硬盤上的Linux,一定要跟我當前的內核是兼容的,那麼安裝完系統以後,剛開始啟動的內核到最後切換到根上,拉起來的這個大的Linux一定是相同內核的,否則就會有兼容性的問題。

這就是如何啟動一個硬盤上的大Linux或在網絡上的一個大Linux,或者是任何其它的比如U盤上的Linux環境你也可以給它拉起來。

當然如果在U盤上的Linux我建議你直接用全部在內存裡的Linux,沒必要再拉一次在網絡上的或者是在硬盤上的。INITRAMFS不可能放得下所有東西。

一般還有一個二次的,就是拉起來Linux在中間的過程,可以說基本上除了內核沒變,其它的環境都變掉了,這就是啟動一個完整的Linux。

03 另類的啟動:KEXEC

下面我們介紹一下另類的啟動。

Linux還有一種啟動方式叫做KEXEC,KEXEC本來是一個Linux的內核的這個開發者,因為他們經常修改內核,修改完了之後要重啟看看,每一次重啟都要經過一個CPU的Reset的冷啟動也好,熱啟動也好,那是很痛苦的事情。

因為Reset這個CPU之後一定先走BIOS,BIOS走完了最後走Grub,最後有個Boot Load,Grub走完了才啟動Linux,因為嫌這個很煩,他們就開發了一個東西,就是在當前的Linux下,我突發奇想,想換一個新內核,他就用這個KEXEC先把一個內核加進來然後跳轉到新內核裡,相當於執行了一次Linux重啟,但是注意跳過了前面的POST和Grub。

這就是那位開發人員懶就開發了一個這個功能,這個功能確實能省很多時間。

如果你要想讓內核能夠這麼加載的話就有一個要求,要求Position Independent Code這段代碼可以再任意加載到任意地址,如果不能加載到任意地址的話,就要求固定地址。

那在當前內核如何加載另外一個內核?跟冷啟動一樣其實有三樣東西,一個是內核文件,先把它加進來,然後INITRAMFS文件也加進來,然後啟動參數主要是指根,總的來說這個KEXEC是一個非常奇妙的啟動Linux方式。大家可以試一下在自己Linux上用它來試一下。

04 Linux kdump模式

最後我們要講一下Linux kdump模式,緊密依賴KEXEC的模式的dump非常重要。


Linux啟動深度分析,5000字乾貨筆記(建議收藏)

dump是什麼?

就是應用CRASH掉的時候,應用當時佔的內存的所有的內容,然後分析應用在哪裡CRASH掉了。系統也一樣,當Linux系統出了某種錯誤,哪怕是硬件出了某種錯誤進行不下去的時候怎麼診斷它,我們需要把它當前的內存的內容全拷貝下來。

如何拷貝呢?

在CRASH的時候,如果沒有kdump來支持,CRASH就hung在那了,如果啟動了kdump,在啟動內核,生產系統內核的時候,有一個參數叫 Crashkernel,Crashkernel等於多少多少,就是預留一塊內存給這個kdump的kernel dump內核轉儲用。預留完後,當生產系統起來,它的一個服務就是把kdump所用的內核加載到這塊預留的區域裡。

為什麼要這樣做呢?

因為系統CRASH的時候再加載內核好像有問題,其實CRASH這些問題啥都做不了,內核已經沒法加載了,動都動不了了,就一定要在生產系統剛起的時候預留一塊區域,這塊區域誰都不能使用,然後把一個內核加在裡面,INITRAMFS加在裡面,那些參數也加在裡面,這樣當這個生產內核跑著跑著要CRASH了,如果這個區域都設置好了它就會直接跳轉到這個區域裡新的內核裡,開始新的內核新的一個Linux環境的啟動。

這個Linux環境會限制在預留的區域裡,在預留的區域裡啟動完之後,它要做的事情就是第一,把前一個環境的比如說100個G也好200個G也好的內容拷貝出來,拷貝到指定的某一個磁盤的目錄下,這就是kdump。

Kdump確實是非常好的想法,它依賴於KEXEC,KEXEC是一個快速的Linux啟動,很多程序員不耐煩那個POST的過程太長,Kdump很好的解決了這個問題。

"


分享到:


相關文章: