作者:曹達士
全國Linux領域貢獻最多的十大工程師之一
X86架構服務器資深專家,30年IT領域經驗
文章摘自聯想超級課
今天我們講一下X86Linux的啟動。
一個計算機的啟動,最主要就是CPU的啟動。CPU啟動就是把這個CPU的裡邊的各個寄存器設置到一個已知的、固定的狀态,并且從這裡開始執行。
01 指針啟動如下圖的右邊這有幾個就是 X86 的話的一些比較清晰可見的寄存器,其中最重要的是 EIP,即指令指針。
指令指針就表示它要執行的下一個指令,當我們 CPU 一發生 Reset 以後,這個EIP作為一個固定的值,就會從這裡開始執行,這裡通常是 BIOS 的程序。
X86 的 CPU 有三種啟動方式:
加電啟動和 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走完了之後,它要做的一件事情就是要找到一個文件,并且把它讀到内存裡,然後開始執行它。
這個時候我們認為post自檢完了,要開始讀取并執行第一個外部程序,一般叫它Boot Load,那麼它這個如何找到并讀取并執行它呢?
第一個問題主要是找到并讀取它,這裡又分幾種,比如
Legacy BIOS 啟動
Legacy BIOS一般都從某個硬盤上啟動,通常在過去這個叫C盤,因為過去隻有一個盤叫C盤,現在當然磁盤很多了,你還是要指定一個啟動盤,從哪個卡上的哪個盤來啟動。
如果是指定了,它就讀你指定的盤,這個硬盤的同一個扇區,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,這些東西跟網卡沒有關系,網卡要做的就是收發報而已。
PXE協議分四步:
第一步,必須有DHCP,先發DHCP包,我要取得IP地址,才能做後面的事情,如果你是想啟動的話,在DHCP包裡面還附加了很多東西,附加了一些标記。
第二步,在包發出去之後,看到這個标記的人,如果他能提供PXE服務,也會響應關于PXE的東西。
PXE響應它什麼呢?有幾個IP地址,這幾個地址是可以提供啟動服務的,而且每個IP地址它什麼文件,都可以告訴你。
最後一步,有了啟動服務器的地址和我要啟動的文件,那麼我就通過TFTP從這個服務器下載這個文件就可以了。
下面這是一檔例子,在标準的DHCP服務裡,附帶提供PXE支持,它支持在哪呢:首先它檢視DHCP包裡面是不是包含這個字段,如果包含這個字段,就告訴你啟動服務器是誰,你去跟他找啟動文件。
光告訴你啟動服務器是誰還不行,還必須告訴你啟動文件是啥,啟動文件根據機型不同它是有區别的,比如你是啟動Power PC,還要啟動mips,還要啟動SUN的SPARC。
02 啟動Linux需要什麼光是X86就有三種架構,一種是64位的UEFI,一種是32位的UEFI,另外一種就是老的i386的PC模式,叫做Legacy BIOS模式。
所以根據包裡面告訴我的架構不同,就反饋你文件名,不同的文件名給你,等于交互完之後就得到兩個:一個是不同IP地址,一個是文件名。通過TFTP給它下載下來開始執行就好了。後面的就跟從硬盤上是一樣的了。
如果是啟動Linux呢?Linux現在需要什麼東西?
Linux 隻需要兩個東西(當然了其實它是需要三個東西),但文件隻需要兩個:一個是内核文件,一個叫做INITRAMFS文件。
我們都知道内核文件肯定需要,任何一個操作系統起來都必須先把内核設置好,啟動好。
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,一定要跟我當前的内核是兼容的,那麼安裝完系統以後,剛開始啟動的内核到最後切換到根上,拉起來的這個大的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非常重要。
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很好的解決了這個問題。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!