Arch 的啟動流程
為了啟動 Arch Linux,必須配置一個與 Linux 兼容的引導加載程序。引導加載程序負責在初始化啟動進程之前,加載好內核和 initramfs,具體過程因 BIOS 和 UEFI 系統而異。
固件是開機時最先執行的程序。
- 本文時常以 BIOS 和 UEFI 代稱固件。
- 勿與 Linux 固件混淆。
統一可擴展固件接口(Unified Extensible Firmware Interface,UEFI)支持讀取分區表和文件系統。UEFI 不從主引導記錄(MBR)中啟動任何引導代碼(無論其是否存在),相反,UEFI 的啟動過程依賴非易失性隨機訪問存儲器(NVRAM)中的引導條目。
UEFI 規範要求支持 FAT12、FAT16 和 FAT32 文件系統(參見 UEFI 規範 2.11 版 13.3.1.1 小節),但每個符合規範的廠商可以選擇添加對其它文件系統的支持;比如,蘋果的固件支持 HFS+ 或 APFS 文件系統。UEFI 的一些實現方案還支持光碟的 ISO 9660 文件系統。
UEFI 會啟動 EFI 應用程式,例如引導加載程序、引導管理器和 UEFI Shell 等等。這些應用程式通常以文件形式存儲在 EFI 系統分區中。廠商可以將其特定文件存儲在 EFI 系統分區中的 /EFI/vendor_name 文件夾下。應用程式可以通過在 NVRAM 中添加引導項或從 UEFI shell 中啟動。
UEFI 規範通過兼容性支持模塊(Compatibility Support Module,CSM)來支持傳統 BIOS 引導。如果在 UEFI 中啟用了 CSM,UEFI 會為所有驅動器生成 CSM 引導項。如果選擇從某一個 CSM 引導項啟動,UEFI 的 CSM 會嘗試從這個磁碟的 MBR 引導代碼啟動。
BIOS,又稱基本輸入輸出系統(Basic Input-Output System),大多數情況下儲存在主板自身的一塊快閃記憶體內,獨立於其它系統存儲。其最早是為 IBM PC 開發,用於處理硬體初始化和啟動過程。從 2010 年起已逐漸被技術上沒有類似限制的 UEFI 替換。
系統上電時,會執行加電自檢(Power-on self-test,POST)。詳細信息可參考 Hugo Landau 的 Modern CPUs have a backstage cast 一文。
- 加電自檢後,UEFI 初始化引導所需的硬體(硬碟、鍵盤控制器等等)。
- 固件讀取 NVRAM 中的引導項,以決定要啟動哪一個 EFI 應用程式,以及從哪啟動(比如從哪一個硬碟和分區)。
- 一個引導項可能對應的只是一塊硬碟。在這種情況下,固件會尋找硬碟上的 EFI 系統分區,並嘗試在後備引導路徑
\EFI\BOOT\BOOTx64.EFI處(在 IA32(32 位)UEFI 的系統上為BOOTIA32.EFI)查找 EFI 應用程式。這就是UEFI 可引導可移除介質的工作原理。
- 一個引導項可能對應的只是一塊硬碟。在這種情況下,固件會尋找硬碟上的 EFI 系統分區,並嘗試在後備引導路徑
- 固件啟動 EFI 應用程式。
- 這可以是一個#引導加載程序,或者是使用 EFISTUB 的 Arch 內核本體。
- 還可以是一些其他的 EFI 應用程式,比如 UEFI shell 或引導管理器(例如 systemd-boot 或 rEFInd)。
如果啟用了安全啟動,啟動過程將會通過簽名驗證 EFI 二進制文件的真實性。
由於每個作業系統或廠商都可以維護自己在 EFI 系統分區中的文件,同時不影響其他系統,所以 UEFI 的多重引導的原理就是啟動不同的、與特定作業系統引導加載程序所對應的 EFI 應用程式。這避免了依賴一個#引導加載程序去加載另一個作業系統的鏈式加載機制。
另請參閱 Arch + Windows 雙系統。
- 上電自檢後,BIOS 初始化引導所需的硬體(硬碟、鍵盤控制器等等)。
- BIOS 啟動在「BIOS 硬碟順序」中第一塊硬碟上的前 440 字節代碼(即主引導記錄引導代碼區域)。
- 引導加載程序在 MBR 引導代碼的第一階段,之後會從下列任意一處啟動第二階段代碼(如果有的話):
- 真正的#引導加載程序啟動。
- 隨後,引導加載程序通過鏈式加載或直接加載作業系統內核的方式加載作業系統。
引導加載程序(boot loader,又稱引導加載器、啟動加載器或啟動引導器)是由計算機固件(BIOS 或 UEFI)啟動的軟體,負責用指定內核參數加載內核和其它外置 initramfs 映像。
引導管理器(boot manager,又稱啟動管理器)讓用戶使用啟動選項菜單或其他方式控制啟動過程——也就是說,僅用於運行其他 EFI 應用程式。
在 UEFI 的情況下,內核本身可以由 UEFI 使用 EFI boot stub 直接啟動。要在引導前編輯內核參數,可以使用引導管理器或是單獨的引導加載程序。
使用 32 位 IA32 UEFI 固件的系統需要使用支持混合啟動模式的引導加載程序。
/boot 目錄下的內核和 initramfs 映像才能成功引導 Arch 系統。也就是說,引導加載程序必須解決從塊設備、堆疊塊設備(LVM、RAID、dm-crypt、LUKS 等)開始,到內核和 initramfs 映像所在文件系統為止的訪問。
因為幾乎沒有引導加載程序支持堆疊塊設備,並且文件系統引入的一些新特性可能尚未有任何引導加載程序支持(例如 archlinux/packaging/packages/grub#7、FS#79857、FS#59047、FS#58137、FS#51879、FS#46856、FS#38750、FS#21733 和 fscrypt 加密目錄),所以用廣泛支持的文件系統(例如 FAT32)單獨創建 /boot 分區通常更可行。
- 由於 GPT 是 UEFI 規範的一部分,因此所有的 UEFI 引導加載程序都支持 GPT 磁碟。在 BIOS 上使用 GPT 磁碟是可行的,可以使用 Hybrid MBR 的「混合引導(hybrid booting)」,或者使用新的純 GPT 協議。但是這個協議可能在某些 BIOS 實現上會出問題,詳情請參考 Rodsbooks。
- 作為 UEFI 規範的一部分,所有 UEFI 引導加載程序都支持安全啟動,但有可能會存在一些限制。
| 名稱 | 固件 | 分區表 | 多重引導 | 文件系統 | 注意 | ||
|---|---|---|---|---|---|---|---|
| BIOS | UEFI | MBR | GPT | ||||
| Clover | 是 | 是 | 否 | 是 | 是 | 可擴展2,5 | 可以在過時 BIOS 系統上模擬 UEFI。 |
| EFI boot stub | – | 是1 | 是 | 是 | – | 繼承自固件2 | 內核是有效的 EFI 可執行文件,可直接從 UEFI 或其它 UEFI 引導加載器啟動。 |
| GRUB | 是 | 是3 | 是 | 是 | 是 | 內置 | 支持 RAID,LUKS(Argon2 PBKDFs 除外)和 LVM(但是不支持精簡配置卷)。平台相關限制請參考 GRUB。 |
| Limine | 是 | 是3 | 是 | 是 | 是 | 有限 | |
| rEFInd | 否 | 是 | 是 | 是 | 是4 | 可擴展2,5 | 支持自動檢測內核和參數,無需明確配置,並支持快速啟動[2]。 |
| Syslinux | 是 | 部分1 | 是 | 是 | 部分 | 有限 | 不支持某些文件系統功能。 只能訪問自身所處的文件系統。 |
| systemd-boot | 否 | 是3 | 手動 | 是 | 是4 | 可擴展2,5 | 無法從 ESP 或擴展引導加載程序分區(XBOOTLDR)以外的分區啟動二進制文件。 支持自動檢測放入 esp/EFI/Linux/ 的統一內核映像。
|
| 統一內核映像 | – | 是3 | 是 | 是 | – | 繼承自固件2 | systemd-stub(7)、內核、initramfs、內核命令行打包而成的 EFI 可執行文件,可直接從 UEFI 固件或另一個引導加載程序加載。 |
| GRUB Legacy | 是 | 否 | 是 | 否 | 是 | 有限 | 停止開發,轉為 GRUB。 |
| LILO | 是 | 否 | 是 | 部分 | 是 | 有限 | 因為局限性(如與 Btrfs、GPT 和 RAID 搭配使用時)已停止開發。 |
- 雖然二進制文件可以被簽名用於安全啟動,但它不會進行後續驗證,從而破壞了信任鏈。
- 文件系統支持是從固件繼承的。UEFI 規範要求支持 FAT12,FAT16 和 FAT32 文件系統,但廠商可選擇添加對其他文件系統的支持。比如說,蘋果 Mac 中的固件支持 HFS+ 文件系統。如果固件提供在啟動時加載 UEFI 驅動程序的接口,則可以通過加載文件系統驅動程序(需單獨獲取)的方式添加對其他文件系統的支持。
- 支持混合模式啟動,即可以在 32 位 IA32 UEFI 固件上啟動 64 位 x86_64 Linux 內核
- 一種啟動管理器。它只能啟動其他的 EFI 應用程式,例如,使用
CONFIG_EFI_STUB=y參數編譯的 Linux 內核映像和 Windows Boot Managerbootmgfw.efi。 - 支持加載 UEFI 文件系統驅動。
另請參見維基百科:引導加載程序比較。
引導加載器會啟動包含內核的 vmlinux 映像。
內核是作業系統的核心。它運行於一個叫內核空間的底層上,負責機器硬體和應用程式之間的交流。在繼續進入用戶空間前,內核會首先執行硬體枚舉和初始化。具體細節請參考zhwp:內核和zhwp:Linux內核。
initramfs(初始內存文件系統,initial RAM file system)映像是一個 cpio 存檔文件,為早期用戶空間(見下文)啟動晚期用戶空間提供了必要的文件。這包括了所有用於定位,訪問和掛載根文件系統的內核模塊、用戶空間工具、相關庫文件、類似 udev 規則的支持文件等。得益於 initramfs 的概念,它可以處理更加複雜的配置場景,例如從外置硬碟啟動,堆疊設備(例如邏輯卷,軟 RAID,壓縮和加密),或是在早期用戶空間中運行一個微型 SSH 伺服器,以供遠程解鎖或為根文件系統執行維護任務。
絕大部分內核模塊都將在初始化流程的後期階段,由 udev 在根切換到根文件系統後加載。
具體流程如下:
-
/下的根文件系統原本是一個空的 rootfs,它是一個特殊的 tmpfs 或 ramfs 實例。這裡就是 initramfs 會解壓到的臨時根文件系統。 - 內核會將其內置 initramfs 解壓到臨時根文件系統下。Arch Linux 官方支持的內核使用空白存檔作為內置 initramfs,即構建內核時的默認行為。
- 然後,內核會按照引導加載器傳遞的命令行參數指定的順序解壓外置 initramfs 映像,覆蓋掉之前內置 initramfs 或其它解壓出來的文件。注意,可以將多個 initramfs 映像合併為一個文件,內核會按照文件內的順序加載映像。
- 如果首個 initramfs 映像未經壓縮,那麼內核會在解包該映像後在
/kernel/x86/microcode/目錄查找 CPU 微碼更新,在/kernel/firmware/acpi/目錄查找 ACPI 表更新。 - 在適用的情況下,在處理完 CPU 微碼和 ACPI 表更新後,內核會繼續解壓剩餘的 initramfs 映像。
- 如果首個 initramfs 映像未經壓縮,那麼內核會在解包該映像後在
initramfs 映像是 Arch Linux 推薦的早期用戶空間配置方法,並可通過 mkinitcpio,dracut 或 booster 來生成。
從 6.13.8 版本開始,官方支持的內核已內置 Btrfs 和 Ext4 的驅動[3]。
因此內核可以直接使用這些文件系統格式的根分區,然後再加載其它需要的外部模塊。但有幾點需要注意:
- 不能使用 GPT 分區自動掛載,必須使用
root內核參數。 - 只能使用
PARTUUID和PARTLABEL作為根分區(root)的塊設備持久化名稱[4]。 -
rootflags的掛載選項比較有限,例如noatime就無法使用[5]。要繞過該問題,可以先以只讀進行掛載(rootflags=ro),然後使用 fstab 進行重新掛載來應用所需的掛載選項。 - 由於 GPT 分區自動掛載不可用,無需啟用 systemd-gpt-auto-generator(8),啟用甚至還會導致問題出現[6],可以通過
systemd.gpt_auto=no將其禁用。
另外,早期微碼加載也必須搭配 initramfs 使用,但沒必要為其構建完整映像,Arch 可以將微碼放置在單獨的 initramfs 文件中,以供單獨使用。
即使沒有提供 initramfs 映像,內核仍會包含一個空映像以供啟動[7],以防止根分區固定出現問題。
早期用戶空間階段(亦稱「initramfs 階段」)在由 #initramfs 映像提供文件的 rootfs 中進行,始於內核以 PID 1 執行 /init。
早期用戶空間的功能可以進行配置,但主要是引導系統到能夠訪問真正根文件系統的狀態,這主要包括:
- systemd-modules-load(8) 加載內核模塊(基於 systemd 的 initramfs),例如掛載真正根文件系統所需的任何塊設備模塊。
- 構建訪問真正根文件系統所需的存儲棧(例如通過 dm-crypt、dm-verity、mdadm、LVM、systemd-repart 等)。
- 解密真正根文件系統(若適用)。
- udev 將塊設備持久化名稱解析為實際設備。
- 加載 DRM 模塊(因為默認啟用的 KMS 早啟動)。
需要注意的是,早期用戶空間不僅僅用於設置真正根文件系統。有些任務只能在掛載真正根文件系統之前執行,例如 fsck和從休眠中恢復。
在早期用戶空間的最後階段,真正根文件系統會被掛載到 /sysroot/(基於 systemd 的 initramfs)或 /new_root/(基於 BusyBox 的 initramfs),然後通過 systemctl switch-root(基於 systemd 的 initramfs)或 switch_root(8)(基於 BusyBox 的 initramfs)切換到真正根文件系統。最後通過執行真正根文件系統中的 init 程序啟動晚期用戶空間。
晚期用戶空間從 init 進程開始。Arch 官方支持的 systemd 基於單元和服務的概念,但這裡描述的功能在很大程度上與其它 init 系統重疊。
init 會為每個虛擬終端(通常有六個)調用一次 getty,它會初始化終端並保護其免受未授權訪問。在提供用戶名和密碼後,getty 會對照 /etc/passwd 和 /etc/shadow 檢查是否正確。如果正確,就接著調用 login(1)。
login 會根據 /etc/passwd 設置環境變量並啟動用戶 shell,從而為用戶配置一個會話。在成功登錄後,啟動登錄 shell 前,login 程序會顯示 /etc/motd(message of the day)的內容,你可以用它來顯示服務條款以提醒用戶你的本地策略,也可以顯示其它提示信息。
用戶的 shell 啟動後,在顯示命令行提示符前,通常會執行一個運行時配置文件(例如 bashrc)。如果用戶帳戶配置為在登錄時自動啟動 X,那麼運行時配置文件會調用 startx 或 xinit,具體內容請參考#圖形會話(Xorg)。
另外,在特定虛擬終端下,init 可配置為啟動顯示管理器,而不是 getty。要達成該效果,需要手動啟用其 systemd 服務文件,之後顯示管理器就會啟動一個圖形會話。
xinit 會調用用戶的 xinitrc 運行時配置文件,後者一般會啟動一個窗口管理器或桌面環境。如果用戶退出了窗口管理器,xinit、startx、shell、login 就會依次中斷,返回到 getty 或顯示管理器。
- Wikipedia:Booting process of Linux
- Inside the Linux boot process
- Rod Smith - Managing EFI Boot Loaders for Linux
- NeoSmart: The BIOS/MBR Boot Process
- Lennart Poettering - Linux Boot Partitions and How to Set Them Up
- Wikipedia:initramfs
- Early Userspace in Arch Linux
- Kernel Newbie Corner: initrd and initramfs
- bootup(7)(主要涉及 systemd 早期用戶空間部分)