Linux工作原理5內核如何啟動

来源:https://www.cnblogs.com/testing-/p/18309128
-Advertisement-
Play Games

一、 Linux發行版本和虛擬機介紹 Linux系統的版本分為兩種,分別是: 內核版 和 發行版。 1) .內核版 由Linus Torvalds及其團隊開發、維護 免費、開源 負責控制硬體 2).發行版 基於Linux內核版進行擴展 由各個Linux廠商開發、維護 有收費版本和免費版本 我們使用L ...


5 內核如何啟動

現在你已經知道了 Linux 系統的物理和邏輯結構、什麼是內核以及如何使用進程。本章將向你介紹內核是如何啟動的。換句話說,你將學習內核如何移動到記憶體中,以及在第一個用戶進程啟動之前內核所做的工作。

啟動過程的簡化視圖如下:

  • 機器的BIOS或引導固件載入並運行引導載入器。
  • 引導載入器在磁碟上找到內核映像,將其載入到記憶體中並啟動。
  • 內核初始化設備及其驅動程式。
  • 內核掛載根文件系統。
  • 內核啟動init 的程式,其進程ID為1,此時為用戶空間啟動。
  • init啟動其他系統進程。

在某些時候,init 會啟動一個允許登錄的進程,通常是在啟動序列的末尾或接近末尾時。

本章主要介紹引導載入器和內核的前幾個階段。第 6 章通過詳細介紹 systemd(Linux 系統中最常見的 init 版本),繼續介紹用戶空間啟動。

5.1 啟動信息

傳統的 Unix 系統在啟動時會產生許多診斷信息,告訴您啟動過程。這些信息首先來自內核,然後來自 init 啟動的進程和初始化程式。然而,這些信息並不漂亮,也不連貫,在某些情況下甚至沒有什麼參考價值。此外,由於硬體的改進,內核的啟動速度比以前快得多;信息閃過的速度太快,很難看清發生了什麼。因此,目前大多數 Linux 發行版都會儘力用閃屏和其他形式的填充物來隱藏啟動診斷信息,以便在系統啟動時分散你的註意力。

查看內核啟動和運行診斷信息的最佳方法是使用 journalctl 命令獲取內核日誌。運行 journalctl -k 會顯示當前啟動時的信息,但也可以使用 -b 選項查看以前的啟動信息。我們將在第 7 章詳細介紹日誌。

如果沒有 systemd,可以檢查 /var/log/kern.log 等日誌文件,或運行 dmesg 命令查看內核環緩衝區中的信息。

下麵是 journalctl -k 命令的示例:

# journalctl -k
Jun 20 15:42:09 sanrui kernel: Booting Linux on physical CPU 0x0000080000 [0x481fd010]
Jun 20 15:42:09 sanrui kernel: Linux version 5.15.0-106-generic (buildd@bos03-arm64-019) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #116-Ubuntu SMP Wed Apr 17 09:19:>
Jun 20 15:42:09 sanrui kernel: efi: EFI v2.70 by EDK II
Jun 20 15:42:09 sanrui kernel: efi: ACPI 2.0=0x2f690018 SMBIOS 3.0=0x2f530000 MEMATTR=0x3370c018 MOKvar=0x261b0000 RNG=0x2f69f698 MEMRESERVE=0x2f670498
Jun 20 15:42:09 sanrui kernel: random: crng init done
Jun 20 15:42:09 sanrui kernel: secureboot: Secure boot disabled
Jun 20 15:42:09 sanrui kernel: ACPI: Early table checksum verification disabled
Jun 20 15:42:09 sanrui kernel: ACPI: RSDP 0x000000002F690018 000024 (v02 HISI  )
...
``

內核啟動後,用戶空間啟動過程經常會產生信息。這些信息可能更難查看和審查,因為在大多數系統中,你不會在一個日誌文件中找到它們。啟動腳本被設計為向控制台發送信息,併在啟動過程結束後被清除。不過,這在 Linux 系統上不是問題,因為 systemd 會捕獲啟動和運行時的診斷信息,這些信息通常會發送到控制台。

##5.2 內核初始化和啟動選項

啟動時,Linux 內核按以下一般順序初始化:

- CPU檢查
- 記憶體檢查
- 設備匯流排發現
- 設備發現
- 輔助內核子系統設置(網路等)
- 根文件系統掛載
- 啟動用戶空間

前兩個步驟並不複雜,但當內核進入設備時,就會出現依賴性問題。例如,磁碟設備驅動程式可能依賴於匯流排支持和 SCSI 子系統支持,這在第3章中已經提到過。之後,在初始化過程中,內核必須在啟動 init 之前掛載根文件系統。

能夠識別啟動過程中的每個階段,對你解決啟動問題和瞭解整個系統將是非常有價值的。不過,許多 Linux 發行版的預設行為通常會使你很難甚至不可能在啟動過程中識別最初的幾個啟動階段,所以你可能只能在這些階段完成並登錄後才能看清楚。

一般來說,除了某些必要組件可能是可載入的內核模塊而不是主內核的一部分外,你不必擔心這些依賴關係。有些機器可能需要在掛載真正的根文件系統前載入這些內核模塊。我們將在第 6.7 節中介紹這個問題及其初始 RAM 文件系統(initrd)的解決方法。

內核會發出某些信息,表明它準備啟動第一個用戶進程:
```sh
Freeing unused kernel memory: 2408K
Write protecting the kernel read-only data: 20480k
Freeing unused kernel memory: 2008K
Freeing unused kernel memory: 1892K

在這裡,內核不僅清理了一些未使用的記憶體,還保護了自己的數據。然後,如果你運行的是一個足夠新的內核,你會看到內核以 init 的身份啟動第一個用戶空間進程:

Run /init as init process
   with arguments:
--snip--

隨後,你應該能看到根文件系統被掛載,systemd 啟動,並向內核日誌發送一些信息:

EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts: (null)
systemd[1]: systemd 237 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN2 +IDN -PCRE2 default-hierarchy=hybrid)
systemd[1]: Detected architecture x86-64.
systemd[1]: Set hostname to <duplex>.

此時,你肯定知道用戶空間已經啟動。

5.3 內核參數

Linux 內核啟動時,會收到一組基於文本的內核參數,其中包含一些額外的系統細節。這些參數指定了許多不同類型的行為,例如內核應產生的診斷輸出量和特定於設備驅動程式的選項。

你可以通過查看 /proc/cmdline 文件,查看傳遞給系統當前運行內核的參數:

$ cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-5.10.0-218.0.0.117.oe2203sp4.aarch64 root=/dev/mapper/openeuler-root ro rd.lvm.lv=openeuler/root rd.lvm.lv=openeuler/swap video=VGA-1:640x480-32@60me cgroup_disable=files apparmor=0 crashkernel=1024M,high smmu.bypassdev=0x1000:0x17 smmu.bypassdev=0x1000:0x15 arm64.nopauth console=tty0 kpti=off

參數要麼是簡單的單字標誌,如 ro 和 quiet,要麼是鍵值對,如 vt.handoff=1。許多參數並不重要,例如用於顯示閃屏的 splash 標誌,但其中一個關鍵參數是 root 參數。這是根文件系統的位置;沒有它,內核就無法正確執行用戶空間啟動。

根文件系統可以指定為設備文件,如下麵的例子:

root=/dev/sda1

在大多數現代系統中,有兩種更常見的選擇。首先,你可能會看到這樣一個邏輯捲:

root=/dev/mapper/my-system-root

你還可能看到一個 UUID(見第 4.2.4 節):

root=UUID=17f12d53-c3d7-4ab3-943e-a0a72366c9fa

這兩個參數都比較好,因為它們不依賴於特定的內核設備映射。

ro參數指示內核在用戶空間啟動時以只讀模式掛載根文件系統。這是正常現象;只讀模式可確保 fsck 在執行任何重要操作之前安全地檢查根文件系統。檢查結束後,啟動進程會以讀寫模式重新掛載根文件系統。

遇到不理解的參數時,Linux 內核會保存該參數。內核隨後會在執行用戶空間啟動時將該參數傳遞給 init。例如,如果你在內核參數中添加了-s,內核就會將-s傳遞給init程式,以表明它應該以單用戶模式啟動。

如果您對基本啟動參數感興趣,請參閱 bootparam(7) 手冊。如果你想找一些非常具體的參數,可以查看 Linux 內核自帶的參考文件 kernel-params.txt。

有了這些基礎知識,你就可以跳到第 6 章,瞭解用戶空間啟動、初始 RAM 磁碟和內核作為第一個進程運行的 init 程式的具體細節。本章其餘部分將詳細介紹內核如何載入到記憶體並啟動,包括如何獲取參數。

5.4 引導載入器(Boot Loaders)

在啟動過程開始時,在內核和 init 啟動之前,引導載入程式會啟動內核。引導載入器的工作聽起來很簡單:從磁碟上的某個地方將內核載入到記憶體中,然後用一組內核參數啟動內核。然而,這項工作比錶面看起來要複雜得多。要理解原因,請考慮一下引導載入器必須回答的問題:

  • 內核在哪裡?
  • 內核啟動時應將哪些內核參數傳遞給內核?

答案是(通常)內核及其參數通常在根文件系統的某處。聽起來內核參數應該很容易找到,但請記住,內核本身尚未運行,通常是內核遍歷文件系統來查找必要的文件。更糟糕的是,通常用於訪問磁碟的內核設備驅動程式也不可用。這就像一個 “先有雞還是先有蛋 ”的問題。問題可能比這還要複雜,但現在,讓我們來看看 Boot Loader 如何剋服驅動程式和文件系統的障礙。

Boot Loader 確實需要驅動程式來訪問磁碟,但與內核使用的驅動程式不同。在個人電腦上,引導載入器使用傳統的基本輸入/輸出系統(BIOS)或較新的統一可擴展固件介面(UEFI)來訪問磁碟。(可擴展固件介面或 EFI 和 UEFI 將在第 5.8.2 節中詳細討論)。現代磁碟硬體包含固件,允許 BIOS 或 UEFI 通過邏輯塊定址 (LBA) 訪問連接的存儲硬體。LBA 是訪問任何磁碟數據的通用、簡單方法,但性能較差。但這並不是問題,因為啟動載入程式通常是唯一必須使用這種模式訪問磁碟的程式;啟動後,內核可以訪問自己的高性能驅動程式。

要確定系統使用的是 BIOS 還是 UEFI,請運行 efibootmgr。 如果得到啟動目標列表,說明系統使用的是 UEFI。如果顯示不支持 EFI 變數,則說明系統使用的是 BIOS。另外,也可以檢查是否存在 /sys/firmware/efi;如果存在,則說明系統使用了 UEFI。

一旦解決了訪問磁碟原始數據的問題,引導載入程式就必須在文件系統中查找所需數據。大多數常見的 Boot Loader 都能讀取分區表,並內置了對文件系統只讀訪問的支持。因此,它們可以找到並讀取內核進入記憶體所需的文件。這種功能使得動態配置和增強 Boot Loader 變得容易得多。Linux 引導載入器並不總是具備這種能力;如果沒有這種能力,配置引導載入器就會更加困難。

一般來說,內核增加新功能(尤其是存儲技術)後, Boot Loader 會增加這些功能的單獨簡化版本來彌補。

5.4.1 引導載入器的任務

Linux 引導載入器的核心功能包括以下功能:

  • 從多個內核中選擇。
  • 在內核參數集之間切換。
  • 允許用戶手動覆蓋和編輯內核映像名稱和參數(例如,進入單用戶模式)。
  • 支持啟動其他操作系統。

自 Linux 內核誕生以來,引導載入器已經變得相當先進,具備了命令行歷史記錄和菜單系統等功能,但內核映像和參數選擇的靈活性始終是一個基本需求。(一個令人驚訝的現象是,有些需求實際上已經減少了。例如,由於可以從 USB 存儲設備執行緊急啟動或恢復啟動,因此很少需要擔心手動輸入內核參數或進入單用戶模式)。當前的 Boot Loader 比以往提供了更強大的功能,如果你正在構建自定義內核或只是想調整參數,這一點會特別方便。

5.4.2 引導載入器概述

以下是您可能會遇到的主要 Boot Loader:

  • GRUB 幾乎是 Linux 系統的通用標準,有 BIOS/MBR 和 UEFI 版本。
  • LILO 最早的 Linux 引導載入器之一。ELILO 是 UEFI 版本。
  • SYSLINUX 可以配置為從多種不同的文件系統運行。
  • LOADLIN 從 MS-DOS 引導內核。
  • systemd-boot 一個簡單的 UEFI 啟動管理器。
  • coreboot(前身為 LinuxBIOS) 一種可包含內核的高性能 PC BIOS 替代程式。
  • Linux Kernel EFISTUB 直接從 EFI/UEFI 系統分區(ESP)載入內核的內核插件。
  • efilinux 一個 UEFI 引導載入器,旨在作為其他 UEFI 引導載入器的模型和參考。

本書幾乎只涉及 GRUB。使用其他 Boot Loader 的理由是它們比 GRUB 配置更簡單、速度更快或提供其他特殊用途的功能。

在引導提示符下輸入內核名稱和參數,可以瞭解引導載入器的很多信息。要做到這一點,你需要知道如何進入引導提示符或菜單。不幸的是,由於 Linux 發行版對引導載入程式的行為和外觀進行了大量定製,這一點有時很難弄明白。通常無法通過觀察啟動過程來判斷發行版使用了哪個引導載入器。

接下來的章節將告訴你如何進入啟動提示符,以輸入內核名稱和參數。一旦你熟悉了這些,你將看到如何配置和安裝 Boot Loader。

5.5 GRUB 簡介

GRUB 是 Grand Unified Boot Loader 的縮寫。我們將介紹 GRUB 2,但還有一個舊版本稱為 GRUB Legacy,已不再使用。

GRUB 最重要的功能之一是文件系統導航,可以輕鬆選擇內核映像和配置。查看 GRUB 菜單是瞭解 GRUB 功能的最佳方式之一。該界面易於瀏覽,但你很可能從未見過它。

要訪問 GRUB 菜單,請在 BIOS 啟動屏幕首次出現時按住 SHIFT,如果系統是 UEFI,則按住 ESC。否則,引導載入器配置可能不會在載入內核前暫停。

嘗試以下方法探索引導載入器:

  • 重新啟動或打開 Linux 系統。
  • 在 BIOS 自檢時按住 SHIFT,或在固件閃屏時按住 ESC,以獲取 GRUB 菜單。(有時這些屏幕不可見,所以你必須猜測何時按下按鈕)。
  • 按 e 查看預設啟動選項的啟動載入程式配置命令。您將看到類似圖 5-2 的內容(可能需要向下滾動才能看到所有細節)。

該屏幕顯示,在此配置中,root 被設置為 UUID,內核映像為 /boot/vmlinuz-4.15.0-45-generic,內核參數包括 ro、quiet 和 splash。初始 RAM 文件系統為 /boot/initrd.img-4.15.0-45-generic。但如果你以前從未見過這種配置,可能會覺得有些困惑。為什麼會有多個 root 引用,它們為什麼不同?為什麼 insmod 會出現在這裡?如果你以前見過它,可能會記得它是 Linux 內核的一項功能,通常由 udevd 運行。

你有理由多看兩眼,因為 GRUB 並不使用 Linux 內核(記住,它的任務是啟動內核)。你所看到的配置完全由 GRUB 內部的功能和命令組成,它存在於自己獨立的世界中。

造成混淆的部分原因是 GRUB 借用了許多來源的術語。GRUB 有自己的 “內核 ”和 insmod 命令,用於動態載入 GRUB 模塊,完全獨立於 Linux 內核。許多 GRUB 命令與 Unix shell 命令相似;甚至還有一個 ls 命令來列出文件。

註意:啟動內核位於邏輯捲上的系統需要一個用於 LVM 的 GRUB 模塊。你可能會在系統中看到這個模塊。

到目前為止,最容易引起混淆的是 GRUB 對 root 一詞的使用。通常,root 指的是系統的根文件系統。在 GRUB 配置中,這是一個內核參數,位於 linux 命令的映像名稱之後。

配置中其他所有提及 root 的地方都是指 GRUB root,它只存在於 GRUB 內部。GRUB 的 “根 ”是 GRUB 查找內核和 RAM 文件系統映像文件的文件系統。

在圖 5-2 中,GRUB 根目錄首先被設置為 GRUB 專用設備(hd0,msdos1),該配置的預設值為 1。在下一條命令中,GRUB 將搜索分區 2 上的特定 UUID。如果找到該 UUID,就會將 GRUB root 設為該分區。

最後,linux 命令的第一個參數(/boot/vmlinuz-. . )是 Linux 內核映像文件的位置 3。GRUB 會從 GRUB 根目錄載入該文件。initrd 命令與此類似,它指定了第 6 章所述初始 RAM 文件系統的文件 4.

您可以在 GRUB 中編輯此配置;這通常是暫時修複錯誤啟動的最簡單方法。要永久解決啟動問題,需要更改配置(參見第 5.5.2 節),但現在讓我們更深入一步,通過命令行界面查看 GRUB 的一些內部信息。

5.5.1 使用GRUB命令行查看設備和分區

如圖 5-2 所示,GRUB 有自己的設備定址方案。例如,找到的第一個硬碟被命名為 hd0,然後是 hd1,以此類推。設備名稱分配可能會改變,但幸運的是,GRUB 可以搜索所有分區的 UUID,找到內核所在的分區,如圖 5-2 中的搜索命令所示。

列出設備
要瞭解 GRUB 如何指代系統中的設備,請在啟動菜單或配置編輯器中按下 c 進入 GRUB 命令行。您將看到 GRUB 提示:

grub>

您可以在此輸入配置中看到的任何命令,但要開始使用,請嘗試使用診斷命令:ls。在沒有參數的情況下,輸出是 GRUB 所知道的設備列表:

grub> ls
(hd0) (hd0,msdos1)

在這種情況下,有一個主磁碟設備 (hd0) 和一個分區 (hd0,msdos1)。如果磁碟上有一個交換分區,也會顯示出來,如 (hd0,msdos5)。分區上的 msdos 首碼說明磁碟包含 MBR 分區表;GPT 則以 gpt 開頭,這在 UEFI 系統上可以找到。(還有更深層次的組合,即分區中包含 BSD 磁碟標簽映射(第三個標識符),但除非在一臺機器上運行多個操作系統,否則通常不必擔心這個問題)。

要獲取更詳細的信息,請使用 ls -l。該命令特別有用,因為它會顯示分區文件系統的 UUID。例如

grub> ls -l
Device hd0: No known filesystem detected – Sector size 512B - Total size 32009856KiB
        Partition hd0,msdos1: Filesystem type ext* – Last modification time
          2019-02-14 19:11:28 Thursday, UUID 8b92610e-1db7-4ba3-ac2f-30ee24b39ed0 - Partition start at 1024Kib - Total size 32008192KiB

該磁碟的第一個 MBR 分區上有一個 Linux ext2/3/4 文件系統。使用交換分區的系統會顯示另一個分區,但從輸出結果中無法判斷其類型。

現在讓我們看看 GRUB 的文件系統導航功能。使用 echo 命令確定 GRUB 根目錄(請記住,這是 GRUB 希望找到內核的位置):

grub> echo $root
hd0,msdos1

要使用 GRUB 的 ls 命令列出根目錄下的文件和目錄,可以在分區的末尾添加一個斜線:

grub> ls (hd0,msdos1)/

由於不方便鍵入實際的 root 分區,可以用 root 變數來代替,以節省時間:

grub> ls ($root)/

輸出結果是該分區文件系統中文件和目錄名的簡短列表,如 etc/、bin/ 和 dev/。現在,GRUB ls 命令的功能完全不同了。以前,你列出的是設備、分區表,或許還有一些文件系統頭信息。現在,你實際上是在查看文件系統的內容。

你可以用類似的方式深入查看分區上的文件和目錄。例如,要檢查 /boot 目錄,請從以下步驟開始:

grub> ls ($root)/boot

使用上下箭頭鍵翻閱 GRUB 命令歷史記錄,使用左右箭頭編輯當前命令行。標準的讀行鍵(CTRL-N、CTRL-P 等)也可以使用。

你還可以使用 set 命令查看當前設置的所有 GRUB 變數:

grub> set
?=0
color_highlight=black/white
color_normal=white/black
--snip--
prefix=(hd0,msdos1)/boot/grub
root=hd0,msdos1

這些變數中最重要的一個是 $prefix,即 GRUB 希望在其中找到配置和輔助支持的文件系統和目錄。接下來我們將討論 GRUB 配置。

使用完 GRUB 命令行界面後,可以按 ESC 鍵返回 GRUB 菜單。或者,如果你已經設置了啟動所需的所有配置(包括 linux 變數和可能的 initrd 變數),可以輸入啟動命令來啟動這些配置。無論如何,啟動你的系統吧。我們將探討 GRUB 配置,這最好在系統完整可用時進行。

5.5.2 GRUB 配置

GRUB 配置目錄通常為 /boot/grub 或 /boot/grub2。它包含中心配置文件 grub.cfg、特定架構目錄(如 i386-pc)(包含尾碼為 .mod 的可載入模塊)以及其他一些項目(如字體和本地化信息)。我們不會直接修改 grub.cfg,而是使用 grub-mkconfig 命令(或 Fedora 上的 grub2-mkconfig)。

首先,快速查看 grub.cfg,瞭解 GRUB 如何初始化菜單和內核選項。你會看到該文件由 GRUB 命令組成,通常以一系列初始化步驟開始,然後是一系列菜單條目,用於不同的內核和啟動配置。初始化過程並不複雜,但開頭的許多條件可能會讓你誤以為並非如此。前半部分包括大量函數定義、預設值和視頻設置命令,如下所示:

if loadfont $font ; then
  set gfxmode=auto
  load_video
  insmod gfxterm
  --snip--

註意:許多變數如 $font 都來自 grub.cfg 開頭附近的 load_env 調用。

在配置文件的後面,你會發現可用的啟動配置,每個配置都以 menuentry 命令開頭。根據前一節所學,你應該能夠閱讀並理解這個示例:

menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-8b92610e-1db7-4ba3-ac2f-30ee24b39ed0' {
        recordfail
        load_video
        gfxmode $linux_gfx_mode
        insmod gzio
        if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
        insmod part_msdos
        insmod ext2
        set root='hd0,msdos1'
        search --no-floppy --fs-uuid --set=root 8b92610e-1db7-4ba3-ac2f-30ee24b39ed0
        linux   /boot/vmlinuz-4.15.0-45-generic root=UUID=8b92610e-1db7-4ba3-ac2f-30ee24b39ed0 ro quiet splash $vt_handoff
        initrd  /boot/initrd.img-4.15.0-45-generic
}

檢查 grub.cfg 文件中是否有包含多個 menuentry 命令的子菜單命令。許多發行版在舊版本的內核中使用子菜單命令,這樣就不會擠占 GRUB 菜單。

如果你想更改 GRUB 配置,不要直接編輯 grub.cfg 文件,因為它會自動生成,系統偶爾會覆蓋它。你可以在其他地方設置新配置,然後運行 grub-mkconfig 生成新配置。

要瞭解配置生成的工作原理,請查看 grub.cfg 的開頭部分。應該有這樣的註釋行:

### BEGIN /etc/grub.d/00_header ###

進一步檢查後,你會發現 /etc/grub.d 中的幾乎每個文件都是一個 shell 腳本,用於生成 grub.cfg 文件的一部分。grub-mkconfig 命令本身就是一個運行 /etc/grub.d 中所有內容的 shell 腳本。請記住,GRUB 本身不會在啟動時運行這些腳本;我們在用戶空間運行這些腳本,生成 GRUB 運行的 grub.cfg 文件。

請以 root 用戶身份親自嘗試。不用擔心會覆蓋當前配置。該命令本身只是將配置列印到標準輸出。

# grub-mkconfig

如果想在 GRUB 配置中添加菜單項和其他命令怎麼辦?簡而言之,你應該在 GRUB 配置目錄(通常為 /boot/grub/custom.cfg)中新建一個 custom.cfg 文件,將你的自定義設置放入其中。

長答案要複雜一些。/etc/grub.d 配置目錄提供了兩個選項: 40_custom 和 41_custom。第一個選項 40_custom 是一個腳本,你可以自己編輯,但它最不穩定;軟體包升級可能會破壞你所做的任何更改。41_custom 腳本更簡單;它只是一系列在 GRUB 啟動時載入 custom.cfg 的命令。如果選擇第二個選項,在生成配置文件時將不會顯示您所做的更改,因為 GRUB 會在啟動時完成所有工作。

註意:文件名前面的數字會影響處理順序;較低的數字在配置文件中排在前面。

自定義配置文件的兩個選項並不是特別廣泛,沒有什麼能阻止你添加自己的腳本來生成配置數據。你可能會在/etc/grub.d目錄中看到一些針對你的特定發行版而添加的內容。例如,Ubuntu 在配置中添加了記憶體測試啟動選項 (memtest86+)。

要寫入並安裝新生成的 GRUB 配置文件,可以使用 grub-mkconfig 的 -o 選項將配置寫入 GRUB 目錄,如下所示:

# grub-mkconfig -o /boot/grub/grub.cfg

像往常一樣,備份舊配置並確保全裝到正確的目錄。

現在我們將進入 GRUB 和 Boot Loader 的一些技術細節。如果你聽膩了引導載入器和內核,請跳至第 6 章。

參考資料

5.5.3 安裝 GRUB

安裝 GRUB 比配置 GRUB 更複雜。幸運的是,您通常不必擔心安裝問題,因為您的發行版會為您處理。不過,如果您要複製或還原可啟動磁碟,或準備自己的啟動順序,可能需要自行安裝。

在繼續之前,請閱讀第 5.4 節,瞭解 PC 如何啟動,並確定是使用 MBR 還是 UEFI 啟動。接下來,構建 GRUB 軟體集,並確定 GRUB 目錄的位置;預設為 /boot/grub。如果您的發行版會為您編譯 GRUB,您可能不需要編譯 GRUB,但如果需要,請參閱第 16 章瞭解如何從源代碼編譯軟體。請確保構建了正確的目標:MBR 或 UEFI 啟動的目標不同(32 位和 64 位 EFI 啟動的目標甚至也不同)。

安裝引導載入器需要您或安裝程式確定以下內容:

當前運行系統看到的 GRUB 目標目錄。如前所述,這通常是 /boot/grub,但如果您要將 GRUB 安裝到另一個磁碟上供另一個系統使用,則可能有所不同。
GRUB 目標磁碟的當前設備。
對於 UEFI 啟動,EFI 系統分區(通常為 /boot/efi)的當前掛載點。
請記住,GRUB 是一個模塊化系統,但為了載入模塊,它必須讀取包含 GRUB 目錄的文件系統。你的任務是構建一個能讀取該文件系統的 GRUB 版本,以便載入其餘配置(grub.cfg)和任何所需的模塊。在 Linux 上,這通常意味著構建一個預載入了 ext2.mod 模塊(可能還有 lvm.mod)的 GRUB 版本。有了這個版本後,你需要做的就是把它放在磁碟的可啟動部分,然後把其他所需的文件放到 /boot/grub。

幸運的是,GRUB 自帶了一個名為 grub-install 的實用程式(不要與 install-grub 混淆,你可能會在一些舊系統上找到它),它可以為你完成安裝 GRUB 文件和配置的大部分工作。例如,如果你的當前磁碟位於 /dev/sda,而你想在該磁碟的 MBR 上安裝 GRUB,並使用當前的 /boot/grub 目錄,請使用此命令:

# grub-install /dev/sda

錯誤安裝 GRUB 可能會破壞系統的啟動順序,因此不要輕易執行此命令。如果擔心,請研究如何使用 dd 備份 MBR,備份當前安裝的任何其他 GRUB 目錄,並確保有一個緊急啟動計劃。

要在當前系統之外的存儲設備上安裝 GRUB,必須手動指定該設備上的 GRUB 目錄,就像當前系統現在看到的那樣。例如,目標設備是 /dev/sdc,而該設備包含 /boot 的根文件系統(例如 /dev/sdc1)已掛載到當前系統的 /mnt。這意味著安裝 GRUB 時,當前系統將看到 /mnt/boot/grub 中的 GRUB 文件。運行 grub-install 時,請告訴它這些文件的位置,如下所示:

# grub-install --boot-directory=/mnt/boot /dev/sdc

在大多數 MBR 系統中,/boot 是根文件系統的一部分,但有些安裝程式會將/boot 放入自己的獨立文件系統中。請確保您知道目標 /boot 位於何處。

UEFI 安裝應該更簡單,因為只需將引導載入器複製到適當的位置即可。但你還需要用 efibootmgr 命令將引導載入器 “宣佈 ”到固件中,也就是將載入器配置保存到 NVRAM 中。如果有的話,grub-install 命令會運行它,所以通常你可以像這樣在 UEFI 系統上安裝 GRUB:

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

這裡,efi_dir 是當前系統中 UEFI 目錄的位置(通常是 /boot/efi/EFI,因為 UEFI 分區通常掛載在 /boot/efi),name 是引導裝載程式的標識符。

不幸的是,安裝 UEFI 啟動載入器時可能會出現很多問題。例如,如果您安裝到的磁碟最終會被安裝到另一個系統中,您就必須想辦法將引導載入器公佈到新系統的固件中。可移動媒體的安裝程式也有不同。

但最大的問題之一是 UEFI 安全啟動。

5.6 UEFI 安全啟動問題

影響 Linux 安裝的一個新問題是如何處理近期 PC 上的安全啟動功能。激活 UEFI 機制後,任何引導載入程式都必須經過可信機構的數字簽名才能運行。微軟要求硬體供應商在出貨 Windows 8 及更高版本的系統時使用安全啟動。其結果是,如果你試圖在這些系統上安裝未簽名的啟動載入程式,固件將拒絕載入程式,操作系統將無法載入。

主要的 Linux 發行版在安全啟動方面沒有問題,因為它們包含簽名的啟動載入器,通常基於 UEFI 版本的 GRUB。通常情況下,UEFI 和 GRUB 之間會有一個小的簽名臨時文件;UEFI 運行臨時文件,臨時文件反過來執行 GRUB。如果你的機器不在可信環境中或需要滿足某些安全要求,防止啟動未經授權的軟體是一項重要功能,因此有些發行版更進一步,要求整個啟動序列(包括內核)都要簽名。

安全啟動系統也有一些缺點,尤其是對於嘗試構建自己的啟動載入器的人來說。你可以在 UEFI 設置中禁用安全啟動要求。不過,這對雙啟動系統來說並不奏效,因為如果不啟用安全啟動,Windows 將無法運行。

5.7 鏈載其他操作系統

UEFI 支持載入其他操作系統相對容易,因為您可以在 EFI 分區中安裝多個引導載入器。然而,舊的 MBR 風格不支持此功能,即使您有 UEFI,您可能仍有一個帶有 MBR 風格引導載入器的單獨分區。GRUB 可以在磁碟的特定分區上載入並運行不同的引導載入器,而不是配置並運行 Linux 內核;這就是所謂的鏈式載入。

要進行連鎖載入,請在 GRUB 配置中創建一個新的菜單項(使用 “生成新配置文件 ”一節中描述的方法之一)。下麵是在磁碟第三個分區安裝 Windows 的示例:

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

+1選項告訴chainloader載入分區第一個扇區的任何內容。你也可以讓它直接載入文件,方法是使用這樣的一行來載入 io.sys MS-DOS 載入器:

menuentry "DOS" {
	insmod chain
	insmod fat
	set root=(hd0,3)
	chainloader /io.sys
}

5.8 引導載入器細節

現在我們來快速瞭解一下 Boot Loader 的內部結構。要瞭解 GRUB 這樣的引導載入器是如何工作的,首先我們要瞭解一下電腦是如何啟動的。由於必須解決傳統 PC 啟動機制的許多不足,啟動載入方案有多種變化,但主要有兩種: MBR 和 UEFI。

5.8.1 MBR 啟動

除了第 4.1 節所述的分區信息外,MBR 還包括一個 441 位元組的小區域,PC BIOS 在開機自檢(POST)後載入並執行該區域。不幸的是,這個空間不足以容納幾乎所有的引導載入程式,因此需要額外的空間,這就是所謂的多級引導載入程式。在這種情況下,MBR 中的初始代碼除了載入引導載入程式的其餘代碼外,什麼也不做。引導載入程式的其餘部分通常被塞入 MBR 和磁碟上第一個分區之間的空間。這並不十分安全,因為任何東西都可以覆蓋那裡的代碼,但大多數引導載入器都這樣做,包括大多數 GRUB 安裝。

這種將引導載入程式代碼放在 MBR 之後的方案對於使用 BIOS 引導的 GPT 分區磁碟無效,因為 GPT 信息位於 MBR 之後的區域。(GPT 的解決方法是創建一個稱為 BIOS 啟動分區的小分區,並使用特殊的 UUID(21686148-6449-6E6F-744E-656564454649),為完整的啟動載入程式代碼提供一個位置。不過,這種配置並不常見,因為 GPT 通常用於 UEFI,而非傳統 BIOS。通常只有在擁有超大磁碟(超過 2TB)的舊系統中才會使用,因為這些磁碟對於 MBR 來說太大了。

5.8.2 UEFI 啟動

PC 製造商和軟體公司意識到傳統的 PC BIOS 有很大的局限性,因此決定開發一種名為 “可擴展固件介面 (EFI) ”的替代軟體。EFI 花了一段時間才在大多數 PC 上流行起來,但如今它已成為最常見的固件介面,尤其是在微軟要求 Windows 安全啟動的今天。目前的標準是統一 EFI (UEFI),它包括內置 shell、讀取分區表和瀏覽文件系統等功能。GPT 分區方案是 UEFI 標準的一部分。

與 MBR 相比,UEFI 系統的啟動方式截然不同。在大多數情況下,它更容易理解。在 UEFI 系統中,可執行的啟動代碼並不駐留在文件系統之外,而是始終存在一個特殊的 VFAT 文件系統,稱為 EFI 系統分區(ESP),其中包含一個名為 EFI 的目錄。在 Linux 系統中,ESP 通常掛載在 /boot/efi,因此你可能會發現大部分 EFI 目錄結構都是從 /boot/efi/EFI 開始的。每個 Boot Loader 都有自己的標識符和相應的子目錄,如 efi/microsoft、efi/apple、efi/ubuntu 或 efi/grub。引導載入程式文件的擴展名為 .efi,與其他支持文件一起存放在這些子目錄中。如果你繼續探索,可能會發現 grubx64.efi (GRUB 的 EFI 版本)和 shimx64.efi 等文件。

註意:ESP 與第 5.8.1 節所述的 BIOS 啟動分區不同,具有不同的 UUID。你應該不會遇到同時使用這兩種啟動分區的系統。

但有一個問題:你不能將舊的引導載入程式代碼放入 ESP,因為舊的代碼是為 BIOS 界面編寫的。相反,你必須提供一個為 UEFI 編寫的引導載入器。例如,使用 GRUB 時,必須安裝 UEFI 版本的 GRUB 而不是 BIOS 版本。而且,正如前面在 “使用 UEFI 安裝 GRUB ”中解釋的,必須向固件發佈新的引導載入器。

最後,如第 5.6 節所述,我們還必須解決 “安全啟動 ”問題。

5.8.3 GRUB如何工作

最後,我們來看看 GRUB 是如何工作的:

  • 電腦 BIOS 或固件初始化硬體,並搜索啟動順序存儲設備以查找啟動代碼。
  • 找到啟動代碼後,BIOS/固件會載入並執行它。這就是 GRUB 開始的地方。
  • GRUB 核心載入。
  • 核心初始化。此時,GRUB 可以訪問磁碟和文件系統。
  • GRUB 識別其引導分區併在此載入配置。
  • GRUB 允許用戶更改配置。
  • 超時或用戶操作後,GRUB 執行配置(如第 5.5.2 節所述,grub.cfg 文件中的命令序列)。
  • 在執行配置的過程中,GRUB 可能會在引導分區中載入額外的代碼(模塊)。其中一些模塊可能是預載入的。
  • GRUB 執行引導命令,載入並執行配置的 linux 命令指定的內核。

由於傳統 PC 啟動機制的不足,GRUB 內核載入序列的第 3 步和第 4 步可能會比較複雜。最大的問題是 "GRUB 內核在哪裡?有三種基本可能性

  • 部分塞在 MBR 和第一個分區的開頭之間
  • 常規分區中
  • 特殊引導分區中:GPT 引導分區、ESP 或其他地方

除了 UEFI/ESP 之外的所有情況下,電腦 BIOS 都會從 MBR 中載入 512 位元組,這就是 GRUB 的起始位置。這一小段(源自 GRUB 目錄中的 boot.img)還不是核心,但它包含核心的起始位置,並從此處載入核心。

不過,如果你有一個 ESP,GRUB 內核就會以文件形式存在。固件可以瀏覽 ESP 並直接執行 GRUB 或其他操作系統載入器中的所有內容。(你可能會在 ESP 中的 GRUB 之前加一個墊片來處理安全啟動,但想法是一樣的)。

不過,在大多數系統中,這還不是全部。在載入和執行內核之前,引導載入器可能還需要將初始 RAM 文件系統映像載入到記憶體中。這就是 initrd 配置參數指定的內容,我們將在第 6.7 節中介紹。不過,在瞭解初始 RAM 文件系統之前,你應該先瞭解一下用戶空間的啟動--下一章就從這裡開始。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 摘要: 使用QT進行SCSI指令操作時遇到問題,0x28讀取正常,但0x2A寫入失敗,原因是系統對0x2A命令的寫入許可權控制嚴格。解決方法是通過FSCTL_LOCK_VOLUME實現獨占訪問,實現對USB設備的寫操作。 問題參考:https://blog.csdn.net/kifea/article ...
  • 痞子衡嵌入式半月刊: 第 105 期 這裡分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發佈一期。 本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。 上期回 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...