banner
EthanYang

Sail's Blog

github

Linux系統啟動時

之前看到了 Linux 的啟動流程的大概描述:

image

我想知道更具體的細節,所以搜尋了一些資料,做一個總結。

一、CPU Reset#

當我們按下電源按鈕後,電源就開始給設備供電,一開始供電並不穩定,主板檢測到後,就會一直給 CPU 發送RESET 信號,此時的 CPU 就會清除寄存器上的殘留數據並進行寄存器的設置:

image

在這之中最重要的寄存器就是CS 和 EIP,CS 中隱式的 Base 會和 RIP 相加,即ffff0000H+fff0H=fffffff0Hffff 0000H + fff0H = ffff fff0 H,存放在這個地址 (也稱為重置向量) 的是一條跳轉指令,其跳轉的目的地址是BIOS 的入口地址

為什麼重置向量不直接設為 BIOS 入口地址,而是要跳轉呢?
X86 芯片一開始是運行在實模式下的,實模式只有 20 位尋址空間(1M),而我們的重置向量是 fff0H,到 ffffH 只有很短的距離,根本放不下 BIOS 程序,所以要跳轉。
至於為什麼要把重置向量放在地址空間的高地址處,是為了給內存騰出更大的空間。

實模式下的內存佈局

image

多核系統下的 CPU Reset#

多核計算機下,一開始要執行某個協議,系統將選取一個 CPU 來執行,其他 CPU 都處於等待狀態,這個被選取的主 CPU 叫 Bootstrpping CPU(BSP),只有 BSP 繼續執行,其它 CPU 等待 BSP 給它們發指令。

二、BIOS 執行#

BIOS 是固定在 EPROM 中的程序,一般由硬件廠商寫死,其負責引導系統的啟動。

POST 加電自檢#

BIOS 首先執行的程序就是 POST,其用於計算機剛接通電源時對硬件部分的檢測,如果自檢中發現不是很嚴重的錯誤,系統會根據檢測代碼給出提示信息或鳴笛警告

為什麼選擇鳴笛的方式來發出警報?
因為以前沒有核顯,顯卡就一定是外設,POST 執行時,顯卡還沒有完成初始化,所以無法將錯誤信息顯示在屏幕上,只能通過聲音來報警。

檢測的過程是逐一進行的,BIOS 廠商對每個設備都提供了一個POST CODE,當對某個設備進行檢測時,就會把這個 POST CODE 裝到診斷的端口,如果沒通過,這個 CODE 就會被保留,進行報警。

初始化設備#

POST 結束後,BIOS 還會調用各外設的 BIOS 進行自檢和初始化,比如顯卡的 BIOS。所有的設備都是這個時候初始化並啟動的。
除了初始化設備,BIOS 這個時候還會初始化中斷向量表。

啟動 Bootloader#

在檢測和初始化各種設備之後,BIOS 會查找用戶自定義的啟動順序,默認是磁盤,當然也可以是光盤和 U 盤(以前重裝操作系統時就會用到光盤和 U 盤):

image

根據順序,BIOS 會把排在最前面的設備的MBR(主引導記錄),即第 0 磁道第一個扇區的 512 字節,讀到RAM 絕對地址 0x7C00 處,並跳轉到這個地址。

MBR 格式
MBR 不屬於任何一個操作系統,其 512 字節的內容如下:

  • 啟動代碼 (446B):檢測分區表的準確性,並將系統控制轉移給硬盤上的 Bootloader 程序(比如 grub)
  • 磁盤分區表 (16 X 4B):DPT,由 4 個分區表組成,每個 16B,說明磁盤分區情況。
  • 結束標誌 (2B)

UEFI
現在說 BIOS,主要指的是 UEFI,而不是傳統的 BIOS。不管是傳統 BIOS 還是 UEFI,都是經過 ROM→RAM→BOOT 的過程,主幹是沒有區別的,那麼我們為什麼需要 UEFI 呢?
可以參考這個回答:UEFI 引導與 BIOS 引導在原理上有什麼區別?

三、Bootloader#

所謂的 Bootloader 程序就是用來加載操作系統內核文件到內存,這類程序的主要功能為:

  1. 從實模式到保護模式,從 16 位尋址空間到 32 位尋址空間,使能段機制。
  2. 從硬盤讀取 ELF 格式的 Kernel(就是跟在 MBR 後面的扇區)並放到內存中的固定位置,這個過程一般分兩步,最終是執行 boot 指令,即加載系統引導菜單 (/boot/grub/menu.lst或 grub.lst),內核 vmlinuz 和 RAM 磁盤 initrd。
    我們這裡以 GNU 的grub為例。grub 可用於選擇操作系統分區上的不同內核,也可用於向這些內核傳遞啟動參數。grub 被載入一般包括兩個步驟:
  3. 裝載基本引導程序 - stage1,其主要功能是裝載第二引導程序
  4. 裝載第二引導程序 - stage2,這第二引導裝載程序用於引出更高級的功能,以允許用戶裝載入一個特定的操作系統,在 grub 中,這步是給用戶一個顯示菜單或者讓用戶輸入命令,該階段引導的最終狀態是執行 boot 命令,將操作系統內核加載進入內存中,進而將控制權轉交給內核。
    當內核加載完成後,內存被映射為:

image

四、Linux 內核設置和啟動#

Linux 內核啟動後,執行的一系列檢測,檢測完成後跳轉到 start_kernel 函數,這個函數會依次初始化各個模塊,比如頁表、中斷向量等,然後變成 0 號進程。

0 號進程會 fork 出 1 號 kernel_init 進程,kernel_init 會執行 init 程序,大致過程如下:

init 程序

  1. init 進程讀取/etc/inittab文件,作用是設定 Linux 的運行等級,決定進程運行模式。
  2. Linux 執行第一個用戶層文件/etc/rc.d/rc.sysinit,該文件功能包括:設定 PATH 運行變量、設定網絡配置、啟動 swap 分區、設定 /proc、系統函數等.
  3. 讀取/etc/modules.conf/etc/modules.d目錄下的文件來加載系統內核模塊。
  4. 啟動一些服務,之後執行/etc/rc.d/rc.local文件。
  5. 最後執行 /bin/login 程序,啟動到登錄界面,讓用戶輸入用戶名和密碼。

完成用戶的啟動後,0 號進程進入 cpu_idle, 變成 idle 進程。到這 Linux 差不多就啟動好了。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。