章節概覽#

本章說明 Linux kernel 從載入記憶體到啟動第一個 user space process 的完整過程。開機流程的簡化版本如下:

  1. 機器的 BIOS 或 boot firmware 載入並執行 boot loader
  2. Boot loader 在磁碟上找到 kernel image,載入記憶體並啟動
  3. Kernel 初始化裝置與驅動程式
  4. Kernel 掛載 root filesystem
  5. Kernel 啟動 init 程式(PID 1),這就是 user space start
  6. init 啟動其餘系統程序
  7. init 啟動登入程序,讓使用者可以登入

本章聚焦在前幾個階段,尤其是 boot loader 與 kernel 的部分。Chapter 6 則接續說明 user space 的啟動。


5.1 Startup Messages#

傳統 Unix 系統在開機時會產生大量診斷訊息,但現代 Linux 發行版通常會用 splash screen 隱藏這些訊息。

查看 kernel 開機與執行時診斷訊息的最佳方式:

# 查看當前開機的 kernel 訊息
journalctl -k

# 查看先前開機的訊息
journalctl -k -b -1

如果沒有 systemd,可以檢查:

  • /var/log/kern.log 日誌檔
  • 使用 dmesg 命令查看 kernel ring buffer 中的訊息

kernel 啟動後,user-space 啟動程序也會產生訊息。在使用 systemd 的系統上,這些訊息會被自動捕捉保存。


5.2 Kernel Initialization and Boot Options#

Linux kernel 啟動時按以下順序進行初始化:

  1. CPU inspection — CPU 檢查
  2. Memory inspection — 記憶體檢查
  3. Device bus discovery — 裝置匯流排探索
  4. Device discovery — 裝置探索
  5. Auxiliary kernel subsystem setup — 輔助子系統設定(如網路)
  6. Root filesystem mount — 掛載根檔案系統
  7. User space start — 啟動 user space

前兩步較單純,但到了裝置探索階段就會出現依賴性問題。例如磁碟裝置驅動程式可能依賴匯流排支援和 SCSI 子系統支援。

某些必要元件可能是 loadable kernel modules 而非主 kernel 的一部分。部分機器需要在真正的 root filesystem 掛載前先載入這些 kernel modules,這就需要 initial RAM filesystem (initrd) 來解決。

Kernel 準備啟動第一個 user process 時,會釋放未使用的記憶體並保護自身資料:

Freeing unused kernel memory: 2408K
Write protecting the kernel read-only data: 20480k

5.3 Kernel Parameters#

Linux kernel 啟動時會接收一組文字形式的 kernel parameters,用來指定系統行為,例如診斷輸出量和裝置驅動程式選項。

查看目前執行中 kernel 的參數:

cat /proc/cmdline

參數格式為:

  • 單字旗標,如 roquiet
  • key=value 配對,如 vt.handoff=1

重要的 kernel 參數#

  • root — 指定 root filesystem 位置,這是最關鍵的參數。沒有它,kernel 無法執行 user space start。可以用裝置檔案、logical volume 或 UUID 指定:
root=/dev/sda1
root=/dev/mapper/my-system-root
root=UUID=17f12d53-c3d7-4ab3-943e-a0a72366c9fa
  • ro — 指示 kernel 以唯讀模式掛載 root filesystem,讓 fsck 可以安全檢查。檢查完成後,開機程序會重新以讀寫模式掛載。

Kernel 遇到不認識的參數時,會將其傳遞給 init 程式。例如加上 -s 參數,kernel 會把 -s 傳給 init,讓系統以 single-user mode 啟動。

如需更詳細的參數資訊,可以參考:

  • bootparam(7) man page
  • kernel 隨附的 kernel-params.txt 參考檔案

5.4 Boot Loaders#

Boot loader 的工作看似簡單:從磁碟某處載入 kernel 到記憶體,然後帶著一組 kernel parameters 啟動它。但實際上相當複雜,因為這是一個「雞生蛋」問題 — kernel 尚未執行,正常的裝置驅動程式和檔案系統都不可用。

Boot loader 使用 BIOSUEFI 來存取磁碟,透過 Logical Block Addressing (LBA) 讀取儲存硬體。LBA 效能不佳,但因為只有 boot loader 需要使用這種模式,所以不成問題。

要判斷系統使用 BIOS 或 UEFI,可以執行 efibootmgr。如果列出 boot targets,表示系統使用 UEFI。或者檢查 /sys/firmware/efi 是否存在。

5.4.1 Boot Loader Tasks#

Linux boot loader 的核心功能包括:

  • 從多個 kernel 中選擇
  • 切換不同的 kernel parameters 組合
  • 允許使用者手動覆蓋和編輯 kernel image 名稱和參數
  • 支援啟動其他作業系統

5.4.2 Boot Loader Overview#

常見的 boot loader:

  • GRUB — Linux 系統上幾乎通用的標準,支援 BIOS/MBR 和 UEFI
  • LILO — 最早的 Linux boot loader 之一(ELILO 是 UEFI 版本)
  • SYSLINUX — 可從多種檔案系統執行
  • LOADLIN — 從 MS-DOS 啟動 kernel
  • systemd-boot — 簡單的 UEFI boot manager
  • coreboot — 高效能的 PC BIOS 替代方案,可包含 kernel
  • Linux Kernel EFISTUB — 直接從 EFI/UEFI System Partition 載入 kernel
  • efilinux — UEFI boot loader 的參考實作

本書主要討論 GRUB


5.5 GRUB Introduction#

GRUB 全名為 Grand Unified Boot Loader,本書涵蓋的是 GRUB 2(非已過時的 GRUB Legacy)。

Figure 5-1: GRUB menu

GRUB 最重要的能力是檔案系統導覽,讓 kernel image 和設定的選擇變得容易。

存取 GRUB 選單的方式:

  • BIOS 系統:開機時按住 SHIFT
  • UEFI 系統:開機時按 ESC

在 GRUB 選單中按 e 可以查看和編輯預設開機選項的設定命令。

Figure 5-2: GRUB configuration editor

GRUB 不使用 Linux kernel,它的工作是啟動 kernel。GRUB 設定中看到的命令(如 insmodrootls)是 GRUB 自己的命令,與 Linux 系統命令完全獨立。GRUB 中的 root 指的是 GRUB 搜尋 kernel 和 RAM filesystem image 的檔案系統,與系統的 root filesystem 不同。

5.5.1 Exploring Devices and Partitions with the GRUB Command Line#

在 GRUB 選單或設定編輯器中按 c 可進入 GRUB 命令列。

列出裝置:

grub> ls
(hd0) (hd0,msdos1)

grub> ls -l
# 顯示詳細資訊,包括 UUID
  • hd0 表示第一個硬碟
  • msdos 前綴表示 MBR 分割表,gpt 前綴表示 GPT 分割表

檔案系統導覽:

grub> echo $root
hd0,msdos1

grub> ls ($root)/
# 列出檔案和目錄

grub> ls ($root)/boot
# 檢視 /boot 目錄內容

查看 GRUB 變數:

grub> set
# 顯示所有變數,包括 $prefix 和 $root

$prefix 變數指向 GRUB 設定和輔助檔案的所在位置。

5.5.2 GRUB Configuration#

GRUB 設定目錄通常在 /boot/grub/boot/grub2,核心設定檔為 grub.cfg

不要直接修改 grub.cfg,因為它是自動產生的,系統更新時會被覆蓋。應使用 grub-mkconfig 命令(Fedora 上為 grub2-mkconfig)來產生新設定。

grub.cfg 的結構:

  • 開頭是初始化步驟(函數定義、預設值、影像設定)
  • 接著是 menuentry 命令定義的各開機選項

自訂 GRUB 設定的方式:

  • /etc/grub.d/ 目錄中加入自訂 script
  • 使用 40_custom(可自行編輯但較不穩定)或 41_custom(載入 /boot/grub/custom.cfg,較簡單穩定)

產生並安裝新設定:

# 僅輸出設定到標準輸出(不會覆蓋現有設定)
grub-mkconfig

# 產生並寫入設定檔
grub-mkconfig -o /boot/grub/grub.cfg

/etc/grub.d/ 中檔名前的數字影響處理順序,數字較小的先處理。

5.5.3 GRUB Installation#

安裝 GRUB 比設定更複雜,但通常發行版的安裝程式會處理。

在 MBR 系統上安裝:

# 安裝到目前磁碟的 MBR
grub-install /dev/sda

# 安裝到外部儲存裝置
grub-install --boot-directory=/mnt/boot /dev/sdc

在 UEFI 系統上安裝:

grub-install --efi-directory=efi_dir --bootloader-id=name

其中 efi_dir 是 UEFI 目錄的掛載點(通常是 /boot/efi/EFI),name 是 boot loader 的識別名稱。

錯誤安裝 GRUB 可能會破壞開機流程。安裝前請備份 MBR 和現有的 GRUB 目錄,並確保有緊急開機方案。


5.6 UEFI Secure Boot Problems#

Secure boot 是較新的 UEFI 功能,要求所有 boot loader 必須經過受信任的機構數位簽章才能執行。Microsoft 要求搭載 Windows 8 及以後版本的硬體啟用 secure boot。

  • 主流 Linux 發行版沒有問題,因為它們包含已簽章的 boot loader(通常基於 UEFI 版 GRUB)
  • 常見做法是使用一個小型已簽章的 shim,UEFI 執行 shim,shim 再執行 GRUB
  • 部分發行版更進一步,要求整個開機鏈(包括 kernel)都經過簽章

如果你在實驗自製 boot loader,可以在 UEFI 設定中停用 secure boot。但注意,這在雙重開機系統中可能會造成問題,因為 Windows 可能需要 secure boot 才能運行。


5.7 Chainloading Other Operating Systems#

UEFI 系統可以在 EFI partition 中安裝多個 boot loader,方便支援多重開機。但在 MBR 系統上,GRUB 使用 chainloading 技術 — 載入並執行另一個分割區上的 boot loader。

在 GRUB 設定中新增 chainload 項目的範例(Windows 安裝在磁碟第三個分割區):

menuentry "Windows" {
    insmod chain
    insmod ntfs
    set root=(hd0,3)
    chainloader +1
}

+1 選項告訴 chainloader 載入分割區第一個 sector 的內容。也可以直接指定檔案路徑來載入。


5.8 Boot Loader Details#

本節深入探討 boot loader 的內部運作。PC 開機機制有兩種主要方式:MBRUEFI

5.8.1 MBR Boot#

MBR 包含一個 441 bytes 的小區域,PC BIOS 在 POST(Power-On Self-Test)後會載入並執行這段程式碼。由於空間極為有限,這導致了 multistage boot loader 的出現:

  • MBR 中的初始程式碼僅負責載入 boot loader 的其餘部分
  • 其餘 boot loader 程式碼通常塞在 MBR 和第一個分割區之間的空間

對於使用 GPT 的磁碟搭配 BIOS 開機,GPT 資訊佔用了 MBR 後方的空間,解決方案是建立一個 BIOS boot partition(具有特殊 UUID)來存放完整的 boot loader 程式碼。

5.8.2 UEFI Boot#

UEFI (Unified Extensible Firmware Interface) 是傳統 PC BIOS 的替代方案,功能更強大:

  • 內建 shell
  • 可讀取分割表和導覽檔案系統
  • GPT 分割方案是 UEFI 標準的一部分

UEFI 開機方式與 MBR 截然不同:

  • 系統上有一個 EFI System Partition (ESP),格式為 VFAT,包含名為 EFI 的目錄
  • ESP 通常掛載在 /boot/efi,目錄結構在 /boot/efi/EFI
  • 每個 boot loader 有自己的子目錄(如 efi/microsoftefi/ubuntuefi/grub
  • Boot loader 檔案有 .efi 副檔名(如 grubx64.efishimx64.efi

ESP 和 BIOS boot partition 是不同的東西,具有不同的 UUID,系統不會同時使用兩者。

5.8.3 How GRUB Works#

GRUB 的運作流程:

  1. PC BIOS 或 firmware 初始化硬體,搜尋開機儲存裝置
  2. BIOS/firmware 找到 boot code 並執行,GRUB 從這裡開始
  3. GRUB core 載入
  4. Core 初始化,此時 GRUB 可以存取磁碟和檔案系統
  5. GRUB 識別其 boot partition 並載入設定
  6. GRUB 給使用者機會修改設定
  7. 逾時或使用者操作後,GRUB 執行 grub.cfg 中的命令序列
  8. 執行過程中,GRUB 可能從 boot partition 載入額外模組
  9. GRUB 執行 boot 命令,載入並執行 kernel

GRUB core 的位置有三種可能:

  • 塞在 MBR 和第一個分割區之間
  • 在一般分割區中
  • 在特殊的 boot partition(GPT boot partition、ESP 等)

在非 UEFI/ESP 的情況下,PC BIOS 從 MBR 載入 512 bytes(源自 GRUB 目錄中的 boot.img),這段程式碼負責找到並載入 GRUB core。