linux開機引導和啓動過程(詳細)(含配置文件解讀)

過程概述

  1. BIOS加電自檢
  2. 加載MBR中的Bootloader
  3. 加載內核,初始化initrd
  4. 加載父進程,執行初始化程序
  5. 打開終端

BIOS開機加電自檢

BIOS(基本輸入輸出系統),該系統存儲於主板的ROM芯片上,計算機在開機時,會最先讀取該系統,然後會有一個加電自檢過程。這個過程其實就是檢查CPU和內存,計算機最基本的組成單元(控制器、運算器和存儲器),還會檢查其他硬件,若沒有異常就開始加載BIOS程序到內存當中。BIOS的功能大概可以分爲3個部分:

  1. 用於電腦剛接通電源時對硬件部分的檢測,也叫做加電自檢(Power On Self Test,簡稱POST)。功能是檢查電腦是否良好,通常完整的POST自檢將包括對CPU,640K基本內存,1M以上的擴展內存,ROM,主板,CMOS存儲器,串並口,顯示卡,軟硬盤子系統及鍵盤進行測試,一旦在自檢中發現問題,系統將給出提示信息或鳴笛警告。自檢中如發現有錯誤,將按兩種情況處理:

    • 對於嚴重故障(致命性故障)則停機,此時由於各種初始化操作還沒完成,不能給出任何提示或信號;
    • 對於非嚴重故障則給出提示或聲音報警信號,等待用戶處理。
  2. 初始化硬件(這個玩過開發板的應該都很清楚)。包括創建中斷向量、設置寄存器、對一些外部設備進行初始化和檢測等,其中很重要的一部分是BIOS設置,主要是對硬件設置的一些參數,當電腦啓動時會讀取這些參數,並和實際硬件設置進行比較,如果不符合,會影響系統的啓動。

  3. 引導操作系統程序BIOS會按照硬盤的引導記錄去查找第一個磁盤頭的MBR信息,並加載和執行MBR中的Bootloader程序,若第一個磁盤不存在MBR,則會繼續查找第二個磁盤(啓動順序可以在BIOS的界面中進行設置),一旦BootLoader程序被檢測並加載內存中,BIOS就將控制權交接給了BootLoader程序(控制權交接其實就是指CPU跳轉到Bootloader程序的內存位置執行)

加載MBR中的BootLoader

MBR(Master Boot Record)主引導記錄,MBR存儲於磁盤的頭部,大小爲512 bytes,組成爲:[446 bytes(存儲BootLoader程序)+64 bytes(存儲分區表信息)+2 bytes(用於MBR的有效性檢查)]。

這個部分就需要分爲3個階段來執行了:

Stage_1

BIOS加載它找到的第一個引導記錄(MBR)中到內存中,並開始執行此代碼。Bootloader必須非常小,因爲它必須連同分區表放到硬盤的第一個 512 字節的扇區中。 在傳統的常規 MBR 中,引導代碼實際所佔用的空間大小爲 446 字節。這個MBR中這個 446 字節的文件通常被叫做引導鏡像(boot.img),其中不包含設備的分區信息,分區是一般單獨添加到引導記錄中。

Stage_1.5

由於boot.img必須做的非常小,所以基本不可能有太多複雜的功能,他唯一能做的就是加載Stage_1.5中的存儲的代碼,也就是core.img(2538925389字節)。該文件通常位於MBR與第一個分區之間,也就是0630\sim63扇區之間(未包括6363),總大小爲62512=3174462*512=31744字節。這個文件,存放了一些通用的文件系統驅動程序,如標準的 EXT 和其它的 Linux 文件系統,如 FATNTFS 等。所以這階段就是負責執行Stage_1.5中存放的驅動程序,並加載相關的驅動程序。

Grub2core.img 遠比老一版的 Grub1 更復雜且更強大。這意味着 Grub2core.img 能夠放在標準的EXT 文件系統內,但是不能放在邏輯卷內。故在Grub2中,Stage_1.5中的文件可以存放於 /boot 文件系統中,一般在 /boot/grub2 目錄下。

Stage_2

進入Stage_2時,所有的文件(內核、initrd等)都已存放於/boot目錄及其幾個子目錄之下。同時,該階段還可以從 /boot/grub2/i386-pc 目錄下加載一些內核運行時模塊。所以Stage_2的主要功能是定位和加載 Linux 內核和initrd到內存中,並轉移控制權。

關於Grub2

linux啓動時按下C即可進入Grub2的命令行模式,這裏你可以自己手動引導內核和initrd的加載。關於手動引導的相關操作,可以自行百度。這裏我們重點來看下配置文件/boot/grub2/grub.cfg,這裏有篇文章已經說得很詳細,這裏就直接與大家分享了:

加載內核並初始化initrd

由於Linux內核是很精簡的,只保留必要的驅動程序,而現實中的設備種類繁多,例如:根文件系統可以存儲在包括IDE、SCSI、USB在內的多種介質上,如果將這些設備的驅動都編譯進內核那該多麼臃腫,所以就乾脆將設備特有的驅動與內核分離,單獨形成initrd(一般自己編譯的系統不需要,發行版需要兼容所以會需要)。需要哪種驅動就提取到initrd中,待啓動時提前加載即可。所以內核啓動一般分兩步:

初始化initrd

linux內核啓動前, bootloader會將存儲介質中的 initrd 文件加載到內存,內核啓動時會在訪問真正的根文件系統前先訪問該內存中的 initrd 文件系統,這個過程將會完成相關驅動模塊的加載,最主要就是加載根文件系統存儲介質的驅動模塊等,這樣內核就可以正常識別設備和真正的根文件系統了。

initrd( boot loader initialized RAM disk),就是由 bootloader 初始化的內存盤,算是一個很mini版的文件系統。

啓動內核

之後,內核會以只讀方式掛載根文件系統,當根文件系統被掛載後,開始裝載第一個進程(用戶空間的進程),執行/sbin/init,之後就將控制權交接給了init程序。如果是使用systemd的話,則是加載systemd進程到內存中。

初始化系統

這一節呢,我們分爲兩個init系統來敘述:

Sysvinit系統

sysvinit 就是 System V 風格的 init 系統,顧名思義,它源於 System V 系列的 UNIX。最初的 linux 發行版幾乎都是採用 sysvinit 作爲 init 系統。sysvinit 用術語 runlevel 來定義 “預訂的運行模式”。比如

  • runlevel 3 是命令行模式,
  • runlevel 5 是圖形界面模式,
  • runlevel 0 是關機,
  • runlevel 6 是重啓。

這個初始化系統的涉及到的配置文件如下(按init進程讀先後順序):

/etc/inittab

####省去多餘註釋#######
###表示當前缺省運行級別爲5(initdefault),即默認以圖形界面啓動

id:5:initdefault:
###啓動時自動執行/etc/rc.d/rc.sysinit腳本(sysinit)

# 這一項表示在系統啓動時執行/etc/rc.d/rc.sysinit程序
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
# 當切換到runlevel3(命令行模式時),執行一下/etc/rc.d/rc這個程序,並傳遞參數3
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4

###當運行級別爲5時,以5爲參數運行/etc/rc.d/rc腳本,init將等待其返回(wait)

l5:5:wait:/etc/rc.d/rc 5
#這個就是同時匹配多個runlevel
l6:23456:wait:/etc/rc.d/rc 6

每個配置項格式如下:

id:runlevel:action:processid:runlevel:action:process

各項含義如下:

  1. id

    id是指入口標識符,它是一個字符串,對於getty或mingetty等其他login程序項,要求id與tty的編號相同,否則getty程序將不能正常工作。

  2. runlevel

    runlevel是init所處於的運行級別的標識.runlevel可以是並列的多個值,以匹配多個運行級別,對大多數action來說,僅當runlevel與當前運行級別匹配成功纔會執行

    • 0:表示關機
    • 1:表示單用戶模式,在這個模式中,用戶登錄不需要密碼,默認網卡驅動是不被加載,一些服務不能用。
    • 2:表示多用戶模式,NFS服務不開啓
    • 3:表示命令行模式
    • 4:這個模式保留未用
    • 5:表示圖形用戶模式
    • 6:表示重啓系統
  3. action

    • respawn:當process終止後馬上啓動一個新的
    • wait:當進入指定的runlevels後process纔會啓動一次,並且到離開這個runlevels終止
    • initdefault:設定默認的運行級別,即我們開機之後默認進入的運行級別,不能是0,6,你懂的
    • sysinit:系統初始化,只有系統開機或重新啓動的時候,這個process纔會被執行一次
    • powerwait:當init接收到電源失敗信號的時候執行相應的process,並且如果init有進程在運行,會等待這個進程完成之後,再執行相應的process
    • powerfail:當init接收到電源失敗信號的時候執行相應的process,並且如果init有進程在運行,不會等待這個進程完成,它會直接執行相應的process
    • powerokwait:電源已經故障,但是在等待執行對應操作的時候突然來電了就執行對應的process
    • powerfailnow:當電源故障並且init被通知UPS電源已經快耗盡執行相對應的process
    • ctrlaltdel:當用戶按下ctrl+alt+del這個組合鍵的時候執行對應的process
    • boot:只有在引導過程中,才執行該進程,但不等待該進程的結束;當該進程死亡時,也不重新啓動該進程
    • bootwait:只有在引導過程中,才執行該進程,並等待進程的結束;當該進程死亡時,也不重新啓動該進程
    • off:如果process正在運行,那麼就發出一個警告信號,等待20秒後,再通過殺死信號強行終止該process。如果process並不存在那麼就忽略該登記項
    • once:啓動相應的進程,但不等待該進程結束便繼續處理/etc/inittab文件中的下一個登記項;當該進程死亡時,init也不重新啓動該進程
  4. process

    表示啓動哪個程序或腳本或執行哪個命令

/etc/rc.d/rc.sysinit

所有的運行級別下,init依賴/etc/rc.d/rc.sysinit這個腳本對系統進行初始化。按照下面的順序按部就班的初始化系統:

  • 激活 udev 和 selinux
  • 設置定義在 /etc/sysctl.conf中的內核參數
  • 設置系統時鐘
  • 加載 keymaps
  • 啓用交換分區
  • 設置主機名(hostname)
  • 根分區檢查和 remount
  • 激活 RAID 和 LVM 設備
  • 開啓磁盤配額
  • 檢查並掛載所有文件系統
  • 清除過期的 locks 和 PID 文件
  • 最後找到指定 runlevel 下的腳本並執行,其實就是啓動服務。

/etc/rc.d/rc

這個程序就是根據傳入的參數,調用/etc/rc.d/rc0.d~rc6.d中的相應的腳本程序,來完成相應的初始化工作和啓動相應的服務,最後調用/etc/rc.d/rc.local,這個文件裏面可以放一些用戶自定義的啓動腳本等等

Systemd系統

systemd 是 linux 系統中最新的初始化系統(init),它主要的設計目標是克服 sysvinit 固有的缺點,提高系統的啓動速度。

在開始看Systemd進程做的事情之前,請先了解一下開機啓動腳本的編寫和選項含義,這裏直接推薦一篇不錯的文章:編寫systemd下服務腳本。新舊系統運行等級映射關係如下:

SystemV 運行級別 systemd 目標態 systemd 目標態別名 描述
halt.target 停止系統運行但不切斷電源。
0 poweroff.target runlevel0.target 停止系統運行並切斷電源.
S emergency.target 單用戶模式,沒有服務進程運行,文件系統也沒掛載。這是一個最基本的運行級別,僅在主控制檯上提供一個 shell 用於用戶與系統進行交互。
1 rescue.target runlevel1.target 掛載了文件系統,僅運行了最基本的服務進程的基本系統,並在主控制檯啓動了一個 shell 訪問入口用於診斷。
2 runlevel2.target 多用戶,沒有掛載 NFS 文件系統,但是所有的非圖形界面的服務進程已經運行。
3 multi-user.target runlevel3.target 所有服務都已運行,但只支持命令行接口訪問。
4 runlevel4.target 未使用。
5 graphical.target runlevel5.target 多用戶,且支持圖形界面接口。
6 reboot.target runlevel6.target 重啓。
default.target 這個目標態target是總是 multi-user.targetgraphical.target 的一個符號鏈接的別名。systemd 總是通過 default.target 啓動系統。default.target 絕不應該指向halt.targetpoweroff.targetreboot.target。

Systemd進程在被加載到內後,將依次執行五個目標:

  1. /usr/lib/systemd/system/default.target,這個文件鏈接到當前目錄的graphical.target
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
  1. 根據上一步中可以看到default.target強依賴於multi-user.target,所以接下來這步就要執行如下兩個目錄中的目標了:
  • /etc/systemd/system/multi-user.target.wants/
  • /usr/lib/systemd/system/multi-user.target.wants/

大部分的用戶程序的目標都在這兩個目錄下有配置,在這步就一併啓動了

#/usr/lib/systemd/system/multi-user.target.wants/multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
  1. 接下來到了basic.target目標了,內容如下:
  • /etc/systemd/system/basic.target.wants/
  • /usr/lib/systemd/system/basic.target.wants/
#/usr/lib/systemd/system/multi-user.target.wants/basic.target
[Unit]
Description=Basic System
Documentation=man:systemd.special(7)

Requires=sysinit.target
After=sysinit.target
Wants=sockets.target timers.target paths.target slices.target
After=sockets.target paths.target slices.target
  1. sysinit.target會啓動重要的系統服務例如系統掛載,內存交換空間和設備,內核補充選項.
  • /etc/systemd/system/sysinit.target.wants/
  • /usr/lib/systemd/system/sysinit.target.wants/
[Unit]
Description=System Initialization
Documentation=man:systemd.special(7)
Conflicts=emergency.service emergency.target
Wants=local-fs.target swap.target
After=local-fs.target swap.target emergency.service emergency.target
  1. 然後是最後一個目標local-fs.target,它不會啓動用戶相關服務,它只處理底層核心服務,這個target會根據/etc/fstab來執行相關磁盤掛載操作

Systemd系統服務配置修改

  • /usr/lib/systemd/system/xx服務.service:官方默認配置文件,不建議修改這個位置的文件,而應該像下面這項這樣操作;
  • /etc/systemd/system/xx服務.service.d/custom.conf:在 /etc/systemd/system 下面創建與配置文件相同文件名的目錄,但是要加上 .d 的擴展名。然後在該目錄下創建配置文件即可。另外,配置文件最好附文件名取名爲 .conf。 在這個目錄下的文件配置會合併到 /usr/lib/systemd/system/xx服務.service
  • /etc/systemd/system/xx服務.service.wants/*:此目錄內的文件爲鏈接文件,設置相依服務的鏈接。意思是啓動了 xx服務.service 之後,最好再加上這目錄下面建議的服務。
  • /etc/systemd/system/xx服務.service.requires/*:此目錄內的文件爲鏈接文件,設置相依服務的鏈接。意思是在啓動 xx服務.service 之前,需要事先啓動哪些服務的意思。

打開終端

sysvinit系統

在sysvinit系統中,由於/etc/inittab中的配置如下:

1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
# 只有我們進入5運行級別,會打開圖形用戶終端,並且一旦終端終止,就會再創建一個新的
x:5:respawn:/etc/X11/prefdm -nodaemon

在2345運行級別下,會調用/sbin/mingetty啓用6個命令行界面的終端,並且一旦終端終止,就會再創建一個新的。

Systemd系統

在上面初始化的第1步中等待了display-manager.service這個服務就是用於啓動終端界面的,內容如下:

[Service]
ExecStart=/usr/sbin/gdm
ExecStartPost=-/bin/bash -c "TERM=linux /usr/bin/clear > /dev/tty1"
KillMode=mixed
Restart=always
IgnoreSIGPIPE=no
BusName=org.gnome.DisplayManager
StandardOutput=syslog
StandardError=inherit
EnvironmentFile=-/etc/locale.conf
ExecReload=/bin/kill -SIGHUP $MAINPID

[Install]
Alias=display-manager.service
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章