[TOC] 1. Linux啟動過程 Linux的啟動過程是在執行多級初始化過程中啟動一個Linux的安裝,它在許多方面類似於BSD和其他Unix風格的引導過程,從中衍生出來。 引導Linux安裝設計多個階段和軟體組成,包括固件初始化,引導載入程式的執行,L ...
目錄
1. Linux啟動過程
Linux的啟動過程是在執行多級初始化過程中啟動一個Linux的安裝,它在許多方面類似於BSD和其他Unix風格的引導過程,從中衍生出來。
引導Linux安裝設計多個階段和軟體組成,包括固件初始化,引導載入程式的執行,Linux內核映像的載入和啟動,以及各種腳本和守護程式的執行,對於這些階段和組件中的每一個,存在不同的變化和方法,例如:GRUB
、LILO
、SYSLINUX
或Loadin
可用做引導載入程式,而啟動腳本可以是傳統的init
風格,也可以通過現在的方法代替(如systemd
或upstart
)
2. 啟動過程概述
Linux啟動過程的早期階段很大程度取決於電腦體繫結構,IBM PC相容硬體是Linux常用的一種架構,在這些系統上,BIOS
起著很重要的作用,可能在其他系統上沒有精確的類似物,在以下示例中,假設使用與IBM PC
相容的硬體:
BIOS
執行特定於實際硬體平臺的啟動任務,一旦枚舉硬體並正確初始化引導所需的硬體,BIOS
就會從配置的引導設備載入並執行引導代碼。- 引導載入程式通常向用戶提供一個可能的引導選項菜單,並有一個預設選項,該選項在經過一段時間後被選中,一旦做出選擇,引導載入程式就會將內核載入到記憶體中,併為其提供一些參數,並賦予其控制權。
- 內核如果被壓縮,將自動解壓縮,然後,它設置系統功能,如基本硬體和記憶體分頁,並掉調用
start_kernel()
,它執行大部分系統設置(中斷、記憶體管理和其餘部分、設備和驅動程式初始化等),然後它分別啟動空閑進程、調度程式和init
進程,這些進程在用戶空間執行。 init
由shell
(susv,bsd,runit)執行的腳本或二進位組件(systemd,upstart)執行的配置文件組成,init
具有特定的級別(sysv,bsd)或目標(systemd),每個級別都由特定的服務集(守護進程)組成,它們提供何種非操作系統服務和結構,並形成用戶環境,典型的伺服器環境啟動Web伺服器、資料庫服務和網路- 典型的桌面環境以一和名為
display manager
的守護程式開始,該守護程式啟動一個圖形環境,該圖形環境由一個提供圖形堆棧的圖形伺服器和一個提供輸入憑據和選擇會話的登陸管理器組成, 會話是一組程式,例如UI元素(頁面、桌面、小程式等),這些元素可以一起構成一個完整的桌面環境。
3. 引導載入階段
引導載入程式階段因電腦體繫結構而異,由於早期階段並非特定與操作系統,所以在實際模式下執行主引導記錄(MBR
)代碼並載入第一階段引導載入程式時,將考慮啟動x86
和x86-64
體繫結構的基於BIOS
的引導過程,在UEFI
系統中,可以直接執行負載,比如Linux內核,因此,不需要引導載入程式,下麵是一些流行的引導載入程式的摘要:
GRUB 1
- 包括在運行時讀取公共文件系統以訪問其配置文件的邏輯,這使GRUB 1
能夠從文件系統讀取配置文件,而不是將其嵌入到MBR
中,這允許它在運行時配置並以人類可讀的格式指定磁碟和分區而不是依賴於偏移量,它還包含一個命令介面,如果GRUB
配置錯誤或損壞,它可以更容易地修複和修改GRUB
。GRUB 2
- 不同於GRUB 1
,它有兩個(可選三個)階段,能夠自動檢測各種操作系統和自動配置,第一階段載入程式(stage 1)由BIOS從主引導記錄(MBR)載入和執行,或者由分區引導扇區的另一個引導載入程式載入和執行,它的工作是發現和訪問各種文件系統,稍後可以從這些文件系統讀取配置,可選的中間階段載入器(stage 1.5)由第一階段載入器載入執行,以防第二階段載入器不是連續的,或者文件系統或硬體需要特殊處理才能訪問第二階段載入器,第二階段載入器(stage 2)最後載入,並顯示GRUB啟動菜單,允許用戶選擇操作系統或檢查和編輯啟動參數,選擇菜單項並給出可選參數後,GRUB將內核載入到記憶體並將控制權遞給它,GRUB 2還能夠鏈式載入另一個引導載入程式
4. 內核階段
Linux內核處理所有操作系統進程,例如記憶體管理、任務調度、I/O、進程間通信和整個系統控制,它分兩個階段載入,在第一階段,內核(作為壓縮的鏡像文件)被夾在到記憶體中並解壓縮,然後設置一些基本功能,如基本記憶體管理,然後最後一次將控制項切換到主內核和啟動進程,一旦內核完全運行並且作為啟動的一部分,在載入和執行之後,內核尋找要運行的init進程,它(單獨地)設置用戶空間和用戶環境和最終登陸所需的進程,然後,內核本身被允許空閑,這取決來自其他進程的調用。
4.1 內核載入階段
內核通常以鏡像文件的形式載入,用zlib壓縮成zlmage或bzImage格式,它頭部的一個常式執行少量的硬體設置,將鏡像完全解壓到記憶體中,併在配置時記錄任何RAM磁碟,然後,它同通過 /arch/ie86/boot/head和startup_32()(用於基於x86的處理器)進程執行內核啟動
4.2 內核啟動階段
內核的啟動函數(也稱為swapper或進程0)建立記憶體管理(分頁表和記憶體分頁),檢測CPU的類型和任何附加功能,比如浮點數功能,然後通過調用start_kernel()切換到非體繫結構特定的Linux內核功能
start_kernel執行各種初始化函數,他設置中斷處理(IRQ),進一步配置記憶體,啟動init進程(第一個用戶空間進程),然後同故宮cpu_idle()啟動空閑任務,值得註意的是,內核啟動進程還掛載了初始RAM磁碟("initrd"),該磁碟先前在引導階段作為臨時根文件系統載入,initrd允許直接從記憶體載入驅動程式模塊,而不依賴其他設置(例如硬碟)和訪問它們所需的驅動程式(例如SATA驅動程式),靜態編譯到內核中的一些驅動程式和initrd載入的其他驅動程式的這種分割允許使用更小的內核,稍後,通過調用pivot_root()切換根文件系統,該調用將卸載臨時根文件系統,併在訪問實際根文件系統之後使用實際根文件系統替換它,然後收回臨時根文件系統使用的記憶體。
因此,內核初始化設備,將引導載入程式指定的根文件系統安裝為只讀,並運行Init(/sbin/init),它被指定為系統運行的第一個進程(PID=1),內核在安裝文件系統時列印消息,它還可以選擇性地運行initrd,以允許在安裝根文件系統之前處理安裝和設備相關的問題(RAM磁碟之類的)
Red Had認為,這一階段的具體內核過程總結如下:
當內核載入時,它立即初始化和配置電腦的記憶體,並配置附加到系統上的各種硬體,包括所有處理器,I/O子系統和存儲設備,然後,它在記憶體中預定的位置查找壓縮後的initrd鏡像,對其進行解壓、掛載並載入所有必要的驅動程式,接下來,在卸載initrd磁碟鏡像並釋放磁碟鏡像占用的所有記憶體之前,初始化於文件系統相關的虛擬設備,比如LVM或者RAID,然後內核創建一個根分區,以只讀方式掛載根分區,並釋放任何未使用的記憶體,此時,內核被載入到記憶體中並可運行,然後,由於沒有用戶應用程式允許對系統進行有意義的輸入,因此無法對其進行太多操作,initramfs風格的引導與之類似,但與所表述的initrd引導不同。
此時,啟動中斷後,調度器可以控制系統的總體管理,提供搶占式的多任務處理,而init進程將繼續在用戶空間引導用戶環境。
5. 早期的用戶空間
initramfs,也成為早期用戶空間,從Linux內核2.5.26版本開始就提供了,其目的是替換以前內核在啟動過程中執行的儘可能更多的功能,早期用戶空間的典型用途是檢測需要哪些設備驅動程式來載入主用戶空間文件系統從臨時文件系統載入它們。
6. 初始化過程
6.1 SysV init
init是系統中所有進程的父進程,由內核執行,負責啟動所有其他進程,它是所有進程的父進程,而init的父進程已經死亡,它負責在這些進程死亡時獲取它們,由init管理的進程成為作業(jobs),由/etc/init目錄中的文件定義
init的任務時在內核完全運行之後“讓所有東西按照應有的方式運行”,本質上,它建立並操作整個用戶空間,這包括檢查和掛載文件系統,啟動必要的用戶服務,並最終在系統啟動完成時切換到用戶環境,它類似於Unix和BSD的init進程,但在某些情況下,它已經分化或定製了初始化進程。在一個標準的Linux系統中,init是用一個名為runlevel的參數執行的,該參數的值從0到6不等,該參數決定哪些子系統是可操作的,每個運行級別都有自己的腳本,這些腳本將設置或離開給定的運行級別時涉及的各種過程進行編碼,引導過程中需要引用這些腳本,init腳本通常保存在名稱為"/etc/rc..."的目錄中,init的頂層配置文件位於/etc/inittab
在系統引導期間,它檢查是否在/etc/inittab中指定了預設運行級別,如果沒有,則請求運行級別通過系統控制台進入,然後,它繼續為給定的運行級別運行所有相關的引導腳本,包括載入模塊、檢查根文件系統的完整性(根文件系統是只讀掛載的)。然後重新掛載它以實現完全的讀寫訪問,並設置網路。
在生成指定的所有進程之後,init進入休眠狀態,並等待發生以下三種時間之一:開始結束或死亡的過程、電源故障信號或通過/sbin/telinit請求進一步更改運行級別。
6.2 Systemd
systemd的開發人員旨在替換從UNIX System V和Berkeley Software Distribution(BSD)操作系統繼承而來的Linux init系統,與init一樣,systemd也是一個守護進程,它管理其他守護進程,包括systemd,都是後臺進程,Systemd是第一個啟動的守護進程(在引導期間),也是最後一個終止的守護進程(在關閉期間)
最初開發systemd的軟體工程師Lennart Poettering和Kay Sievers視圖在幾個方面超越init守護的效率,它們希望改進表示依賴關係的軟體框架,允許在系統啟動期間並行地進行更多處理,並減少shell的計算開銷。
每個守護進程的Systemd初始化指令都記錄在聲明性配置文件中,而不是shell腳本中,對於進程間通信,systemd使Unix域套接字和D-Bus對正在運行的守護進程可用,Systemd還能夠主動並行化。
以上資源摘自Wiki百科:https://en.wikipedia.org/wiki/Linux_startup_process
7. 詳細的瞭解
要想通透的瞭解linux的啟動過程,以下是必須瞭解的理論知識:
BIOS
(Basic Input/Output System)基本輸入輸出系統,也被稱為System BIOS,ROM BIOS或PC BIOS,是非易失性的固件使用在開機過程中執行硬體的初始化(開機啟動),併為操作系統和程式提供運行時服務。BIOS固件預安裝在個人電腦的系統板上,是第一個打開電源時運行的軟體。
大多數BIOS實現專門設計用於與特定電腦或主板模型一起使用,通過與構成互補系統晶元組的各種設備連接。最初,BIOS固件存儲在PC主板上的ROM晶元中。在現代電腦系統中,BIOS內容存儲在快閃記憶體中,因此可以在不從主板上移除晶元的情況下重寫它。這允許對BIOS固件進行簡單的最終用戶更新,從而可以添加新功能或修複錯誤,但這也會使電腦感染BIOS rootkit。
FIRMWARE
固件,就是寫入只讀存儲器中(ROM)的程式,例如電腦主板上的基本輸入/輸出系統BIOS,專業人士都叫他固件。由於早期固件晶元採用ROM設計,所以無法更改,隨著技術的不斷發展,可以使用EPROM(可擦寫可編程只讀存儲器)或EEPROM(帶電可擦可編程只讀存儲器)進行修改或升級。
POST
(Power-On Self-Test)加電自檢是在電腦或其他數字電子設備開機後立即通過固件或軟體程式執行的過程。POST的結果可以顯示在作為設備一部分的面板上,輸出到外部設備,或者存儲以供將來通過診斷工具檢索,例如自檢可能檢測到顯示器不起作用,因此可以提供指示燈或揚聲器以將錯誤代碼顯示為一系列閃爍、蜂鳴聲或嗶嗶聲...。在電腦的情況下,POST是設備的於啟動序列的一部分,如果它們成功完成,則調用引導載入程式代碼以載入操作系統。(POST是BIOS功能的一個主要部分)
Bootstrapping
(Booting)引導,是啟動電腦的過程,特別是在啟動其軟體方面,該過程涉及一系列階段,其中在每個階段載入更小,更簡單的程式,然後執行下一階段更大、更複雜的程式,引導是一系列事件。例如,當電腦開機時,電腦首先執行一個存儲在只讀存儲器(ROM)中相對小的程式,以及少量所需的數據,以訪問RAM或磁碟,並將磁碟中的"操作系統"和"數據"載入到RAM中,以進行後續的操作。啟動此序列的小程式被稱為"引導載入程式"或"引導程式",這個小程式的唯一工作是載入其他數據和程式,然後從RAM中執行。
ROM
(Read-Only Memory)只讀存儲器,是電腦和其他電子設備中使用的一種非易失性存儲器,存儲在ROM中的數據只能慢慢修改,比較困難,或者根本不可能更改,因此它主要用來存儲固件(不會經常更新的軟體)。嚴格來說,只讀存儲器是指硬連接的存儲器,製造後不能改變,在許多應用程式中,這樣的記憶體永遠無法更改是一個缺點,因為錯誤和安全的問題無法修複,並不能添加新特性。每個存儲程式電腦可以使用一種非易失性存儲的形式(即,在移除電源時保留其數據的存儲器)來存儲在通電時運行的初始程式(成為引導Bootstrapping)
RAM
(Random-access memory)隨機存取存儲記憶體,易失性存儲器:當電腦關閉時,RAM磁碟會丟失存儲的數據,除非將記憶體安排為備用電池電源。所謂隨機存取,旨的是存儲器中的數據被讀取或寫入時,所需要的時間與這段信息所在的位置或所寫入的位置無關,相對的,讀取或寫入順序訪問(Sequential Access)存儲設備中的信息時,其所需要的時間與位置就會有關係。它主要用來存放操作系統、各種應用程式、數據等。
根據存儲單元的工作原理不同,RAM分為靜態RAM和動態RAM。
靜態隨機存儲器(SRAM)
特點是工作速度快,只要電源不撤除,寫入SRAM的信息就不會消失,不需要刷新電路,同時在讀出時不破壞原來存放的信息,一經寫入可多次讀出,但集成度較低,功耗較大。SRAM一般用來作為電腦中的高速緩衝存儲器(Cache)
動態隨機存儲器(DRAM)
它將每一位數據存儲在集成電路內的單獨的微小電容器中。電容器可以充電或放電; 取這兩種狀態來表示的比特的兩個值,通常稱為0和1的電荷在電容器慢慢泄漏掉,所以無需干預,晶元上的數據會很快丟失,為防止這種情況,DRAM需要外部存儲器刷新電路周期性地重寫電容器中的數據,將它們恢復到原來的電荷。與不需要刷新數據的靜態隨機存取存儲器(SRAM)相比,該刷新過程是動態隨機存取存儲器的定義特征。
MBR
(Master Boot Record)主引導記錄,是一種特殊類型的引導扇區,在一開始就被劃分出來,在512位元組的主引導扇區中,MBR只占用了其中的446個位元組,另外的64個位元組交給了 DPT(Disk Partition Table硬碟分區表),最後兩個位元組“55,AA”是分區的結束標誌。MBR扇區可能包含用於定位活動分區並調用其捲引導記錄的代碼。MBR保存有關如何在該介質上組織包含文件系統的邏輯分區的信息,MBR還包含可執行代碼用作已安裝操作系統的載入程式。通常是將控制權交給GRUB等其他引導載入程式,或者與每個分區的捲引導記錄(VBR)一起使用。此MBR通常成為引導載入程式。MBR中分區表的組織將磁碟的最大可定址存儲空間限製為2TiB(2^32^x512位元組),由於這種限制,基於MBR分區方案正在被新電腦中的GUID分區表(GPT)方案取代,GPT可以與MBR共存,以便為舊系統提供某種有限形式的向後相容性。MBR不存在於非分區媒體上,例如,軟盤或快閃記憶體之類的其他存儲設備,因為這樣的媒體被視為單個分區。
GTP
(GUID Partition Table)GTPS是用於佈局的標準分區表的物理電腦存儲設備,諸如硬碟驅動器或固態驅動器,使用全球唯一標識符(GUID),由於主引導記錄(MBR)分區表的限制性,它構成了同意擴展固件介面(UEFI)標準的一部分。但它也用於某些BIOS系統,它使用32位進行邏輯塊定址(LBA)傳統的512位元組磁碟扇區。
所有現代個人電腦操作系統都支持GPT。一些(包括x86架構上的macOS和Microsoft Windows)僅支持在具有EFI固件的系統上從GPT分區啟動,但FreeBSD和大多數Linux發行版可以從具有舊版BIOS固件介面和EFI的系統上的GPT分區啟動。
VBR
(Volume Boot Record)捲引導記錄,(也稱為捲引導扇區,分區引導記錄或分區引導扇區),它可以在分區數據存儲設備(如硬碟)或為分區設備(如軟盤)上找到,並包含存儲在設備其他部分的引導程式(通常,但不一定是操作系統)的機器碼。是尚未分區的數據存儲設備的第一個扇區。或在已分區設備上,它是設備上單個分區的第一個扇區,整個設備的第一個扇區是包含分區表的主引導記錄(MBR)。捲引導記錄中的代碼可以由機器的固件直接調用,也可以通過主引導記錄(MBR)或引導管理器(GRUB)中的代碼間接調用,MBR和VBR中的代碼本質上以相同的方式載入。
通過引導管理器調用VBR稱為鏈載入,將各個操作系統安裝的引導代碼的副本從VBR中複製,並存儲在硬碟文件中,在引導載入程式詢問用戶引導哪個操作系統後,從文件載入相關的VBR內容。
擴展:Windows與Linux
例如一些雙啟動系統,流行的Linux和Windows操作系統,每個都包含在自己的分區中,Windows不支持多引導系統。但是大多數的Linux安裝程式都適用於雙啟動(儘管需要一些分區知識)。但是在重新啟動時,引導載入程式將僅識別兩個操作系統中的一個,
安裝Linux引導管理器/載入程式(通常是GRUB)作為主引導記錄指向的主引導載入程式有一些優點,正確安裝Linux引導載入程式可以找到Windows操作系統,但是Windows引導管理器無法識別Linux安裝(Windows也不會與Linux文件系統交互)。
所以,通常建議將Windows安裝到第一個主分區,Windows和Linux的引導載入程式通過計算分區來識別帶有數字的分區,(註意,Windows和Linux都根據分區表中分區的順序來計算分區,這可能與磁碟上分區的順序不同)在硬碟末端添加或刪除分區將對它之前的任何分區都沒有影響,但是如果在硬碟驅動器的開頭或中間添加或和刪除分區,則後續分區的編號可能會更改,如果系統分區的編號發生更改,則需要重新配置引導載入程式,以便操作系統引導並正常進行。
必須將Windows安裝到主分區(必須是第一個分區),Linux可以安裝在硬碟驅動器上任何位置的分區中,也可以安裝到邏輯分區(擴展分區內),如果Linux安裝在擴展分區內的邏輯分區中,則它不受主分區中的更改影響。
GRUB2
GNU GRUB (GNU GRand Unified Bootloader, 通常稱為GRUB)是GNU Project的一個引導載入程式包。GRUB是參考實現了的自由軟體基金會的多引導規範,它提供了用戶的選擇來引導多個操作系統安裝在電腦上,或者選擇一個特定的內核在一個特定的操作系統的分區可用的配置。打開電腦是,BIOS會找到已配置的主要可引導設備,並從主引導記錄(MBR)載入並初始引導程式(引導載入器,即GRUB)。
GRUB的兩個主要版本是常用的:
- GRUB版本1,稱為GRUB,只在Linux發行版,其中一些仍然在使用,並支持老版本的流行,例如CentOS的 5 。
- GRUB 2是從頭開始編寫和旨在取代其前身,現在被大多數Linux發行版使用。
拿CentOS7舉例,這是我個人理解的啟動過程:
(1)引導階段
第一步:打開電源後(開機),電腦相對較笨,只能讀取部分存儲空間(ROM),由於ROM的特性,這時CPU處於只讀模式。在ROM中,存儲了一個稱為固件的小程式(BIOS),它進行加電自檢,檢測硬體等(POST),最主要的是,允許訪問其他類型的記憶體(硬碟或RAM)。BIOS存儲了磁碟的啟動順序,BIOS按照順序去找MBR。找到後會將MBR中的引導代碼複製到RAM中(是一個446位元組的boot.img鏡像)。並轉移控制權(CPU)。MBR本身還包含一個64位元組的分區表,描述了分區的大小和其他屬性。剩餘的2位元組用於磁碟簽名。
(此步驟稱為階段1)
第二步:由於MBR引導程式不能理解文件系統的結構,因此它必須定位core.img然後載入到RAM中,並轉移控制權。core.img代碼必須位於MBR和第一個分區之間的位置,由於歷史技術原因,第一個分區的扇區開始位置是63
,可以計算(一個扇區為512bytes),62*512=31744
,然後減去MBR占用的一個扇區31744-512=31232
,而一個core.img的大小也就26676
左右。在此註意,還有一個diskboot.img,它是core.img的第一個扇區(即整個磁碟的第二個扇區),其唯一目的是載入core.img中通用的文件系統的驅動,例如FAT,NTFS,EXT等。,這時就可以直接訪問文件系統了(/boot文件)。(此步驟為階段1.5)
第三步:core.img進入32位保護模式,然後載入/boot/grub2/i386-pc/normal.mod
,normal.mod用於解析/boot/grub2/grub.cfg
文件,可選擇載入模塊(例如,用於圖形UI)並顯示菜單。然後通過用戶的選擇(或者預設)將指定的內核載入到RAM中。並將控制權交給內核(此步驟為階段2)
(2)內核階段
載入階段:內核同通常以鏡像文件的形式載入,用zlib壓縮成zImage或bzImage格式,內核作為壓縮的鏡像文件,將解壓縮自身。
啟動階段:內核會立即初始化和配置電腦記憶體,並配置附加到系統上的各種硬體,包括所有處理器,I/O子系統和存儲設備。然後它在記憶體中預定的位置查找壓縮後的initrd(臨時根文件系統),對其進行解壓,然後內核啟動虛擬記憶體交換器(它是一個內核進程)並創建一個根分區,以只讀方式掛載,該initrd中包含各種可執行程式和驅動程式。然後內核創建初始用戶空間進程執行/sbin/init,這裡的init其實就是/usr/lib/systemd的鏈接文件(即調用systemd,其PID為1)。等待systemd將實際的根文件系統掛載後,再將initrd這個臨時的根文件系統卸載。
在Centos7的/boot文件中有一個initrd-plymouth.img鏡像文件,你可以解壓看看:
[root@www ~]# cp /boot/initrd-plymouth.img ./initrd.img.gz [root@www ~]# gunzip initrd.img.gz [root@www ~]# cpio -i -d < initrd.img 2663 blocks [root@www ~]# ls bin etc initrd.img lib64 sbin usr
(3)初始化階段
systemd是所有進程的父進程。也是一個守護進程,它管理其他守護進程。包括systemd,都是後臺進程,systemd是第一個啟動的守護進程(在引導期間),也是最後一個終止的守護進程(在關閉期間)。
首先systemd按照/etc/fstab文件中的配置進行掛載。再根據運行級別(通過/etc/systemd/system/default.target
鏈接文件得知運行級別)然後啟動該運行級別的一系列的依賴(詳細的啟動順序可使用命令systemd-analyze plot > boot.html
然後用瀏覽器查看)。最後執行getty.target啟動終端和登陸程式。至此,整個過程結束。
以上是我個人理解,但還是有很多問題,例如最後,getty.target開啟了終端,那麼它是調用/bin/login還是getty.target本身就和/bin/login一樣,這裡還是很迷。