參考資料: An introduction to the Linux boot and startup processes 這篇隨筆,可以理解為是對這篇英文文章的翻譯與個人理解、筆記的整合。 擴展閱讀: GNU GRUB - Wikipedia systemd - Wikipedia BIOS in ...
參考資料:
An introduction to the Linux boot and startup processes
這篇隨筆,可以理解為是對這篇英文文章的翻譯與個人理解、筆記的整合。
擴展閱讀:
BIOS interrupt call - Wikipedia
Multiboot specification - Wikipedia
Master boot record - Wikipedia
Best Couple of 2016: Display manager and window manager
淺析 Linux 初始化 init 系統,第 1 部分:SysV init
前言
理解Linux是如何啟動的對於配置和針對Linux啟動失敗的排錯是至關重要的。
本篇文章大體上把Linux啟動過程分成了兩部分:引導(boot)和啟動(startup)。
引導:從電腦開機至內核初始化完畢啟動systemd程式的這個階段。
啟動:從systemd程式啟動至系統完全啟動完畢的這個階段。
引導階段主要涉及了引導裝載程式(boot loader),對於我所學習的CentOS系列的系統來說,主要涉及的均是GRUB這個引導裝載程式。
- 0.x系列的版本,是老的版本,也叫做GRUB 1/Legacy,主要用於以前的Linux發行版,例如CentOS 5/6。
- 2.x系列的版本,是新的版本,也叫做GRUB 2,現在主流的例如CentOS 7上會使用它。雖然是新版的GRUB,版本號差別也不是很大,但是GRUB 2幾乎是完全重寫的GRUB!
啟動階段主要涉及了系統初始化進程的方式(init 進程/系統)。
- CentOS 5:System V init,可簡稱System V或者SysV。
- CentOS 6:Upstart。
- CentOS 7:Systemd。
這3種進程初始化方式,配置文件的寫法有較大的不同。
本篇隨筆,所涉及的引導和啟動的版本是當前的主流版本,即GRUB 2和Systemd。下文如果沒特別說明或者GRUB單獨出現的話,那麼GRUB就表示GRUB 2了。
將引導和啟動的過程,更細緻的劃分的話,如下所示。
- Boot
- BIOS POST
- Boot loader(GRUB 2)
- Stage 1
- Stage 1.5
- Stage 2
- Kernel initialization
- Startup
- Systemd
引導過程(boot)
想要啟動引導過程的話,主要有兩種方式。一種是電腦處於關機狀態,用戶物理性開機;另一種是電腦處於開機狀態,用戶通過GUI或者CLI程式性重啟(當然如果有物理重啟按鈕,也可以物理性重啟)。
譯者註:我也不曉得為何作者要說這句簡單的話。
BIOS POST
引導的第一步和Linux系統是無關的,所有的OS都會有這個步驟。這步是BIOS中提供的POST(Power On Self Test,開機自檢)功能,主要用於檢測硬體是否可正常工作。
如果POST檢測失敗的話,則引導終止,後續的步驟也不會再有了,系統啟動失敗。
POST檢測成功,確保硬體可正常工作後,BIOS會釋出BIOS中斷(BIOS interrupt,詳見擴展閱讀)INT 13H,它用於在所有已連接的可引導設備上定位引導扇區(boot sector)。第一個找到的包含有效引導記錄(boot record)引導扇區會被載入入記憶體當中,並且將控制權傳遞給從引導扇區中載入的代碼。
引導扇區是引導裝載程式的第一個階段(stage)。現代大多數的Linux發行版中採用三種引導裝載程式,分別是LILO、GRUB 1和GRUB 2,GRUB 2是當前階段最新也是最常用的引導裝載程式。
GRUB 2
GRUB 2的全稱是“GRand Unified Bootloader, version 2”,它使得電腦可以找到OS的內核文件並將其載入記憶體中。
GRUB被設計用於相容多引導規範(multiboot specification,詳見擴展閱讀),這個規範使得它可以引導許多的Linux和自由OS;它還可以鏈式載入(chain load,詳見擴展閱讀)專有(proprietary)OS的引導記錄。
GRUB還可以允許用戶從某個Linux發行版中選擇不同的內核來引導。這樣在某個內核更新發生了錯誤,或者更新使得某些重要的軟體使用出現異常的時候,就可以引導之前版本的內核來正常工作。
GRUB 1的配置文件是/boot/grub/grub.conf,GRUB 2的配置文件是/boot/grub2/grub.cfg。紅帽系的Linux,從Fedora 15和CentOS/RHEL 7開始將GRUB升級到了GRUB 2版本。GRUB 2和GRUB 1在功能上是相同的,但是經過了幾乎完全的重寫,所以是要優於GRUB 1的。
GRUB 1和2都是有三個階段(stage),本文會大致闡述這三個階段。不過,關於GRUB具體是如何配置的,不在本文的範圍中。
GRUB 2在官方中並不是使用這3個階段,即stage 1/1.5/2,這個應該是GRUB 1所使用的,不過使用這3個階段來闡述GRUB 2,也是可以的。
譯者註:在擴展閱讀中,GRUB 2應該是4個階段。
Stage 1
當BIOS POST的自檢階段通過後,BIOS從已連接的設備上尋找可用的引導記錄,引導記錄一般位於磁碟的MBR上。stage 1的引導記錄叫做boot.img。
MBR位於磁碟的第一個扇區sector 0,大小為512B,扣除64B的分區信息和2個位元組的boot signature,僅剩下446B的空間。這個空間非常小,所以boot.img的代碼量也必須很小,它不會包含文件系統的驅動(即沒有識別文件系統相關的代碼),boot.img的唯一作用就是找到並載入stage 1.5。為了完成stage 1的任務,stage 1.5的位置必須位於stage 1自身和第一個分區之間。如圖所示。
By Shmuel Csaba Otto Traian, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=28427221
將stage 1.5載入入記憶體後,stage 1就將控制權轉交給了stage 1.5了。
譯者註:在我的CentOS 7系統上,該文件位於/boot/grub2/i386-pc/boot.img,大小為512B,理論上是應該小於446B才對。
Stage 1.5
如上所述,stage 1.5位於stage 1和第一個分區之間,即sector 1~62,總容量為512*62=31,744。
譯者註:上面的圖片stage 1.5的容量為32,256B,比31,744多了一個512B,不懂為什麼。
這個階段的文件是core.img,作者的該文件大小是25,389B,我的是26,664(/boot/grub2/i386-pc/core.img)。可見該階段還可以有許多的剩餘容量。
這個階段主要是包含了各種各樣的文件系統驅動程式,例如EXT家族、XFS、NTFS等。只有具備了文件系統驅動,才可以基於文件的路徑和名稱(例如/boot/vmlinuz-3.10.0-862.el7.x86_64)來查找/載入某個文件。
剩餘的容量,應該是讓開發人員自己擴展、增加想要載入的文件系統驅動程式的。
這個階段所載入的文件系統驅動,就是用於識別stage 2的文件所在的文件系統的。
GRUB 2的stage 1.5中的core.img要比GRUB 1版本中的更加複雜和強大,因此GRUB 2中的stage 2可以位於標準的EXT系列文件系統,但不能是在LVM分區上。
標準的stage 2的文件的位置是在/boot文件系統上,準確地說,是/boot/grub2。
Stage 2
該階段的文件位於/boot/grub2目錄中。該階段不算前2個階段有鏡像文件(image file:boot.img和core.img),它主要由許多運行時按需載入的內核模塊文件構成(位於/boot/grub2/i386-pc目錄)。
該階段的主要功能是定位並載入內核文件,而後將控制權轉交給內核。內核文件的名稱均以vmlinuz開頭,可在/boot目錄下找到當前系統已安裝的內核。
[root@C7 ~]# ls -l /boot/vmlinuz-* -rwxr-xr-x. 1 root root 6224704 Sep 27 2018 /boot/vmlinuz-0-rescue-341d78e441db4d8b985b51fc31f40be9 -rwxr-xr-x. 1 root root 6224704 Apr 21 2018 /boot/vmlinuz-3.10.0-862.el7.x86_64 [root@C7 ~]# file /boot/vmlinuz-* /boot/vmlinuz-0-rescue-341d78e441db4d8b985b51fc31f40be9: Linux kernel x86 boot executable bzImage, version 3.10.0-862.el7.x86_64 ([email protected]) #1 SMP , RO-rootFS, swap_dev 0x5, Normal VGA /boot/vmlinuz-3.10.0-862.el7.x86_64: Linux kernel x86 boot executable bzImage, version 3.10.0-862.el7.x86_64 ([email protected]) #1 SMP , RO-rootFS, swap_dev 0x5, Normal VGA
GRUB有一個預引導菜單,可以讓用戶選擇想要引導的內核,包含了救援(rescue)模式,配置得當的話還包含恢復(recovery)模式。
Kernel
內核是一種可自我展開的壓縮格式的文件,它們和RAM disk鏡像文件、硬碟的設備映射都位於/boot目錄下。
/boot/initramfs-0-rescue-341d78e441db4d8b985b51fc31f40be9.img /boot/initramfs-3.10.0-862.el7.x86_64.img /boot/System.map-3.10.0-862.el7.x86_64
當內核被載入記憶體執行,自我展開完畢後,它會啟動systemd進程。此時引導過程就結束了,正式進入啟動過程。
註:引導過程的結束時,Linux仍然無法執行任何生產任務。因為生產任務是需要具體的進程來完成的,而systemd進程只是所有生產任務進程的最上級父進程(系統的第一個進程)。
啟動過程(startup)
Systemd
systemd是Linux系統中的第一個進程,即最頂級的父進程。它的任務是啟動系統中的其他進程使得Linux可以進入生產環境得狀態。包含掛載文件系統、啟動和管理服務。和啟動順序無關的systemd任務不在本文的討論範圍之內。
首先,systemd根據/etc/fstab掛載文件系統,包含swap文件和分區。此時systemd就可以訪問位於/etc目錄下的配置文件了,包含自己的配置文件。它使用自己的配置文件(/etc/systemd/system/default.target)來決定要將系統啟動為哪種狀態(state)或者target。default.target僅僅是一個到真實target文件的字元鏈接文件。對於桌面工作站來說,是鏈接到graphical.target(相當於System V init中的運行級別5)。對於伺服器來說,一般是鏈接到multi-user.target(相當於System V init中的運行級別3)。emergency.target則相當於單用戶模式。
註意:target和服務是systemd的單元(unit)。
Table 1是systemd的target和System V init的運行級別的對照表格。systemd提供了target別名(alias)用於向後相容。target別名允許腳本和管理員使用SysV的命令(例如init 3)來切換運行級別,當然,這些命令會傳遞給systemd解析與執行。
SystemV Runlevel |
systemd target |
systemd target aliases |
Description |
halt.target |
Halts the system without powering it down. |
||
0 | poweroff.target | runlevel0.target |
Halts the system and turns the power off. |
S | emergency.target |
Single user mode. No services are running; filesystems are not mounted. This is the most basic level of operation with only an emergency shell running on the main console for the user to interact with the system. |
|
1 | rescue.target | runlevel1.target |
A base system including mounting the filesystems with only the most basic services running and a rescue shell on the main console. |
2 | runlevel2.target |
Multiuser, without NFS but all other non-GUI services running. |
|
3 | multi-user.target | runlevel3.target |
All services running but command line interface (CLI) only. |
4 | runlevel4.target |
Unused. |
|
5 | graphical.target | runlevel5.target |
multi-user with a GUI. |
6 | reboot.target | runlevel6.target |
Reboot. |
default.target |
This target is always aliased with a symbolic link to either multi-user.target or graphical.target. systemd always uses the default.target to start the system. The default.target should never be aliased to halt.target, poweroff.target, or reboot.target. |
每個target在其配置文件中會說明依賴關係,有依賴就有啟動的先後順序之分,被依賴方啟動失敗,那麼依賴方也會啟動失敗(依賴方依賴於被依賴方)。這些依賴一般是系統要運行於某個功能級別下(例如web伺服器、DB伺服器、緩存伺服器等)所需要的服務。當target配置文件中所有的依賴都正常啟動完畢後,系統會運行於那個target級別下。
Systemd也會去查找SysV配置目錄下是否含有啟動腳本,如果有的話,也會載入的。基本上在CentOS 7上,服務已經都使用systemd風格的配置文件來管理了,不過在SysV配置目錄下還有一個網路相關的配置文件,可能只是用作一個學習案例吧,或者網路服務在CentOS 7上依然使用SysV管理(概率較低)。
[root@C7 ~]# ls -l /etc/rc.d/init.d/ total 40 -rw-r--r--. 1 root root 18104 Jan 3 2018 functions -rwxr-xr-x. 1 root root 4334 Jan 3 2018 netconsole -rwxr-xr-x. 1 root root 7293 Jan 3 2018 network -rw-r--r--. 1 root root 1160 Apr 11 2018 README
接下來我們來看一張文本圖,Figure 1。這張圖來源於bootup(7)。展示了systemd的啟動流程。
local-fs-pre.target | v (various mounts and (various swap (various cryptsetup fsck services...) devices...) devices...) (various low-level (various low-level | | | services: udevd, API VFS mounts: v v v tmpfiles, random mqueue, configfs, local-fs.target swap.target cryptsetup.target seed, sysctl, ...) debugfs, ...) | | | | | \__________________|_________________ | ___________________|____________________/ \|/ v sysinit.target | ____________________________________/|\________________________________________ / | | | \ | | | | | v v | v v (various (various | (various rescue.service timers...) paths...) | sockets...) | | | | | v v v | v rescue.target timers.target paths.target | sockets.target | | | | v \_________________ | ___________________/ \|/ v basic.target | ____________________________________/| emergency.service / | | | | | | v v v v emergency.target display- (various system (various system manager.service services services) | required for | | graphical UIs) v | | multi-user.target | | | \_________________ | _________________/ \|/ v graphical.target
sysinit.target和basic.target可以看作是一個檢測點(checkpoint)。儘管systemd的設計目標之一是並行啟動系統服務,但是依然存在某些特定的服務和target是需要在其他服務和target啟動之前啟動的。直到所有被該檢測點依賴的服務和target被滿足後,那麼該檢測點才可以通過。
因此直到所有被依賴的單元完成後,才可以到達sysinit.target。所有的那些單元,掛載文件系統、配置swap文件、啟動udev、建立隨機生成器種子(random genorator seed)、初始化低級設備、如果有文件系統需要加密的話則配置加密服務。不過在sysinit.target內部,上述的這些單元是可以並行執行的。也就是說systemd支持單元在某個target內部的並行執行。
sysinit.target完成了一些底層的功能,即較接近硬體層,慢慢地會往高層發展,接近用戶層,即basic.target。這個階段涉及的unit,如圖所示。
最後就可以啟動multi-user.target和graphical.target這兩個我們熟悉的用戶級別的target了。註意:multi-user.target必須啟動成功,才可以啟動graphical.target。
Figure 1中,加粗帶下劃線的target,是常用的啟動target,到達這些target,就表示系統已經啟動完畢了。multi-user.target啟動完畢後展示了文本模式的登錄界面,而graphical.target則展示了圖形化的登陸界面,想瞭解關於圖形化登陸界面的話,可查閱擴展閱讀中的Best Couple of 2016: Display manager and window manager。