Linux——用戶空間的啓動

用戶空間的啓動

init介紹

init是Linux上的一個用戶空間程序。和其他系統程序一樣,你可以在/sbin目錄下找到它。它主要負責啓動和終止系統中的基礎服務進程,但其較新的版本功能更多一些。

Linux系統中,init有以下三種主要的實現版本。

  • System V init:傳統的順序init(Sys V 讀做 sys-five)爲Red Hat Enterprise Linux和其他的Linux發行版使用
  • systemd: 新出現的init,很多Linux發生版已經或者正在計劃轉向systemd。
  • Upstart:Ubuntu上的init,但是現在Ubuntu已經計劃轉向systemd

還有一些其他版本的init,特別是在嵌入式系統中。例如,Android就有它自己的init。BSD系統也有它們自己的init,不過在目前的Linux系統中很少見到了(一些Linux發行版通過修改System V init配置來遵循BSD樣式)。

因爲System V init和其他老版本的程序依賴於一個特定的啓動順序,每次只能執行一個啓動任務。這種方式中的依賴關係很簡單,然而性能卻不怎麼好,因爲啓動任務無法並行。另一個限制是你只能執行啓動順序規定的一系列服務。如果你安裝了新的硬件,或者需要啓動一個新的服務,該版本的init並不提供一個標準的方法。systemd和Upstart試圖解決性能方面的問題,爲了加快啓動速度,它們允許很多服務並行啓動。它們各自的實現差異很大。

  • systemd 是面向目標的,你定義一個你要實現的目標以及他的依賴條件,systemd負責滿足所有依賴條件以及執行目標。systemd還可以將該目標推遲到確實有必要的時候再啓動。
  • Upstart:完全不同,它能夠接受信息,根據接收到的信息來運行任務,並且產生更多消息,然後運行更多任務,以此類推。

systemd和Upstart init系統還爲啓動和跟蹤服務提供了更高級的功能。在傳統的init系統中,服務守護進程是通過腳本文件來啓動。一個腳本文件負責啓動一個守護程序,守護程序脫離腳本自己運行。你需要使用ps命令或其他定製方法來獲得守護程序的PID。Upstart和systemd則與此不同,它們可以從一開始就將守護程序納入管理,提供正在運行程序的更多信息和權限。

因爲新的init系統不是基於腳本文件,所以配置起來也相對簡單。System V init腳本包含很多相似的命令來啓動、停止和重啓服務,而在systemd和Upstart中沒有這麼多冗餘,這讓你更加專注於服務本身,而非腳本命令。

systemd和Upstart都提供一定程度的即時服務,而不是像System V init那樣在啓動時開啓所有需要的服務。它們根據實際需要開啓相應的服務。這並不是什麼新概念,傳統的inetd守護程序就有,只不過新的實現更爲完善

systemd和Upstart都對System V提供了向後兼容,如支持運行級別(runlevel)的概念。

System V 運行級別

在Linux系統中,有一組進程自始至終都在運行(如crond和udevd)。System V init中把這個狀態叫作系統的運行級別,使用數字060\sim 6來表示。系統幾乎全程運行在單個運行級別中,但是當你關閉系統的時候,init就會切換到另一個運行級別,有序地終止系統服務,並且通知內核停止。

使用who -r命令來查看系統的運行級別。

[root@juechen ~]# who -r
         run-level 3  2020-05-17 15:54

結果顯示系統的當前運行級別是3,還有運行級別起始 時間和日期。

運行級別有幾個作用,最主要的是區分系統的啓動、關閉、單用戶模式和控制檯模式等這些不同的狀態。

三個init版本都支持它,但systemd和Upstart將其視爲已經過時的特性。對它們來說,保留運行級別只是爲了啓動那些只支持System V init腳本的服務。

systemd

systemd init是Linux上新出現的init實現之一。除了負責常規的啓動過程,systemd還包含了一系列的Unix標準服務,如cron和inetd。

這個init的一個重要特性是:

它可以延遲一些服務和操作系統功能的開啓,直到需要它們時再開啓

systemd啓動時的運行步驟:

  1. systemd加載配置信息
  2. systemd判定啓動目標,通常是default.target
  3. systemd判定啓動目標的所有依賴關係
  4. systemd激活依賴的組件並啓動目標
  5. 啓動之後,systemd開始響應系統消息。並激活其組件

單元和單元類型

systemd最有特色的地方是它不僅僅負責處理進程和服務,還可以掛載文件系統、監控網絡套接字和運行時系統等。這些功能我們稱之爲單元,它們的類別稱爲單元類型,開啓一個單元稱爲激活

  • 服務單元:控制Unix上的傳統服務守護進程。
  • 掛載單元:控制文件系統的掛載
  • 目標單元:控制其餘的單元,通常是通過將它們分組的方式。

默認的啓動目標通常是一個目標單元,它依賴並組織了一系列的服務和掛載單元。可以使用systemctl dot命令來創建一個依賴關係樹形圖

systemd中的依賴關係

啓動時和運行時的依賴關係實際比看上去複雜得多,因爲嚴格的依賴關係非常不靈活。 Unix的啓動任務容錯能力很強,一般的錯誤不會影響那些標準服務的啓動。爲了滿足靈活和容錯的要求,systemd提供了大量的依賴類型和形式。

基本類型有以下幾個:

  • Requires:表示不可缺少的依賴關係。如果一個單元有此類型的依賴關係,systemd會嘗試激活被依賴的單元,如果失敗,systemd會關閉被依賴的單元。
  • Wants:表示只用於激活的依賴關係。單元被激活時,它的Wants類型的依賴關係也會被systemd激活,但是systemd不關心激活成功與否。
  • Requisite:表示必須在激活單元前激活依賴關係。systemd會在激活單元前檢查其Requisite類型依賴關係的狀態。如果依賴關係還沒有被激活,單元的啓動也會失敗。
  • Conflicts:反向依賴關係。如果一個單元有Conflict類型的依賴關係,且它們已經被激活,systemd會自動關係它們。同時啓動兩個有反向依賴關係的單元會導致失敗。

Wants是一種很重要的依賴關係,它不會將啓動錯誤擴散給其他單元。systemd文檔鼓勵我們儘可能使用這種依賴關係。原因顯而易見:它讓系統容錯性更強,有點像傳統的init。

你還可以設定反向的依賴關係。例如,如果要將單元A設定爲單元B的Wants依賴,除了在單元B的配置中設置Wants依賴關係,你還可以在單元A的配置中設置反向依賴關係WantedBy。同樣的還有RequiredBy。設定反向依賴除了編輯配置文件外,還涉及其他的一些內容。

用systemctl命令來查看單元的依賴關係:

systemctl show -p type unit

默認情況下,systemd會在啓動單元的同時啓動其所有的Requires和Wants依賴組件。理想情況下,我們試圖儘可能多、儘可能快地啓動服務以縮短啓動時間。

你可以使用下面的依賴關鍵字來設定順序。

  • Before:當前單元會在Before會列出的單元之前啓動。例如,如果Before=bar.target出現在foo.target中,systemd會先啓動foo.target,然後是bar.target
  • After:當前單元在After中列出的單元之後啓動。

如果單元中的依賴條件爲false,單元不會被啓動,不過依賴條件只對其所在的單元有效。如果你啓動的單元中包含依賴條件和其他依賴關係,無論依賴條件爲true還是false,systemd都會啓動依賴關係。

systemd配置

systemd配置文件分散在系統的很多目錄中,不止一處。但主要是分佈在兩個地方:系統單元目錄(全局配置,一般是/usr/lib/systemd/system)和系統配置目錄(局部配置,一般是/etc/systemd/system)。

記住這個原則即可:不要更改系統單元目錄,因爲它由系統來維護。可以在系統配置目錄中保存你的自定義設置。在選擇更改/usr還是更改/etc時,永遠選擇/etc

查看當前的systemd配置的搜索路徑

systemctl -p UnitPath show

你可以使用以下命令來查看系統單元和配置目錄:

pkg-config systemd –-variable=systemdsystemunitdir
pkg-config systemd --variable=systemdsystemconfdir

單元文件

單元文件是由XDG桌麪條目規範(XDG Desktop Entry Specification,.desktop文件,類似Windows中的.ini文件)演變而來,[]中的是區塊名稱,每個區塊包含變量和變量值。

看Fedora系統中/usr/lib/systemd/system目錄下的media.mount單元文件

[Unit]
Description=Media Directory
Before=local-fs.target
[Mount]
What=tmpfs
Where=/media
Type=tmpfs
Options=mode=755,nosuid,nodev,noexec

上面有兩個區塊,

區塊[Unit]包含單元信息和依賴信息,該單元被設定爲在local-fs.target單元之前啓動。

區塊[Mount]表示該單元一個掛載單元,包含掛載點信息、文件系統類型和掛載選項

What=變量 定義了掛載的設備或者設備的UUID

[Unit]
Description=OpenSSH server daemon
After=syslog.target network.target auditd.service
[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

這是一個服務目標,詳細信息在[Service]區塊中,包括服務如何準備就緒、如何啓動和重新啓動。

開啓單元和[Install]區塊

sshd.service單元文件中的[Install]區塊很重要,因爲它告訴我們怎樣使用systemd的WantedBy和RequiredBy依賴關係。它能夠開啓單元,同時不需要任何對配置文件的更改。正常情況下systemd會忽略[Install]部分。然而在某種情況下,系統中的sshd.service被關閉,你需要開啓它。你開啓一個單元的時候,systemd讀取[Install]區塊。這時,開啓sshd.service單元就需要systemd去查看multi-user.target的WantedBy依賴關係。相應地,systemd在系統配置目錄中創建一個符號鏈接來指向sshd.service。

ln -s '/usr/lib/systemd/system/sshd.service' '/etc/systemd/system/multi-user.target.wants/sshd.service'

注意,該符號鏈接創建於被依賴的單元所對應的子目錄中(multi-user.target的子目錄)。

[Install]區塊通常對應系統配置目錄(/etc/sytemd/system)中的.wants和.requires目錄。不在單元配置目錄(/usr/lib/systemd/system)中也有.wants目錄,你可以在單元文件中創建無關[Install]區塊的符號鏈接。這種方法讓你可以不用更改單元文件就能夠加入依賴關係,因爲單元文件有可能被系統更新覆蓋。

開啓(enable)單元和激活(activate)單元不同。開啓單元是指你將其安裝到systemd配置中,做一些在重啓後會保留的非永久的更改。不過你並非總是需要明確地開啓單元。

變量和說明符

sshd.service單元文件還包含了一些變量,如systemd傳遞過來的$OPTIONS和$MAINPID環境變量。當你使用systemctl激活單元時,可以用$OPTIONS變量爲sshd設定選項。$MAINPID是被追蹤的服務進程。

說明符(specifier)是單元文件中另一種類型變量的機制,前綴爲%。例如%n代表當前單元的名稱,%H代表當前主機名。

systemd操作

我們主要通過systemctl命令與systemd進行諸如激活服務、關閉服務、顯示狀態、重新加載配置等的交互操作。

最基本的命令主要用於獲取單元信息。例如,使用list-units命令來顯示系統中所有激活的單元(實際上這是systemctl的默認命令,你不需要指定list-units部分):

systemctl list-units

輸出的結果是典型的Unix列表形式,如下所示:

UNIT LOAD ACTIVE SUB DESCRIPTION
mysqld.service loaded active running MySQL 8.0 database server

該命令的輸出有很多信息,因爲系統中有大量的激活單元。由於systemctl會將長單元名截短,可以使用--full選項來查看完整的單元名。使用--all選項查看所有單元(包括未激活的)。

另一個很有用的systemctl操作是獲得單元的狀態信息。下例是典型的狀態命令和輸出結果

[root@juechen ~]# systemctl status mysqld.service
● mysqld.service - MySQL 8.0 database server
   Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-05-17 15:54:44 CST; 6 days ago
 Main PID: 1024 (mysqld)
   Status: "Server is operational"
    Tasks: 39 (limit: 11516)
   Memory: 474.0M
   CGroup: /system.slice/mysqld.service
           └─1024 /usr/libexec/mysqld --basedir=/usr

May 17 15:54:36 juechen systemd[1]: Starting MySQL 8.0 database server...
May 17 15:54:37 juechen mysql-check-socket[911]: Socket file /var/lib/mysql/mysql.sock exists.
May 17 15:54:37 juechen mysql-check-socket[911]: No process is using /var/lib/mysql/mysql.sock, which means it is a garbage, so it will be removed automatically.
May 17 15:54:44 juechen systemd[1]: Started MySQL 8.0 database server.

你可以使用systemd start、stop和restart命令來激活、關閉和重啓單元。但如果你更改了單元配置文件,你可以使用以下兩種方法讓systemd重新加載文件。

  • systemctl reload unit:只重新加載unit單元的配置。
  • systemctl daemon-reload:重新加載所有的單元配置。

在systemd中,我們將激活、關閉和重啓單元稱爲任務(job),它們本質上是對單元狀態的變更。你可以用以下命令來查看系統中的當前任務:

systemctl list-jobs

如果已經運行了一段時間,系統中可能已經沒有任何激活的任務,因爲所有激活工作應該已經完成。然而,在系統啓動時,如果你很快登錄系統,你可以看到一些單元正在慢慢被激活,如下所示:

JOB UNIT TYPE STATE
1 graphical.target start waiting
2 multi-user.target start waiting
71 systemd-…nlevel.service start waiting
75 sm-client.service start waiting
76 sendmail.service start running
120 systemd-…ead-done.timer start waiting

在systemd中添加單元

在systemd中添加單元涉及創建和激活單元文件,有時候還需要開啓單元文件。將單元文件放入系統配置目錄/etc/systemd/system,這樣你就不會將它們與系統自帶的配置混淆起來,它們也不會被系統更新覆蓋了。

我們來創建兩個目標,其中一個依賴於另一個。

  1. 創建一個名爲test1.target的單元
[Unit]
Description=test 1
  1. 創建test2.target,其依賴於test1.target:
[Unit]
Description=test 2
Wants=test1.target
  1. 激活test2.target單元(test1.target作爲依賴關係也會被激活)

systemctl start test2.target

  1. 驗證兩個單元都被激活:

systemctl status test1.target test2.target

刪除單元

使用下面的步驟刪除單元

  1. 必要時關閉單元

systemctl stop unit

  1. 如果單元中包含[Install]區塊,則通過關閉單元來刪除依賴的符號鏈接:

systemctl disable unit

  1. 這個時候就可以刪除單元文件了

systemd 進程跟蹤和同步

對於啓動的進程,systemd需要掌握大量的信息和控制權。最大的問題是啓動一個服務的方式有多種。這樣導致的後果是,可能會叉分(fork)出許多新實例,甚至還可能會將其作爲守護進程並且同原始進程脫離開。

systemd引進了控制組,它是Linux內核的一個可選特性,爲的是提供更好的進程跟蹤。

啓動行爲有以下兩種:

  • Type=simple:服務進程不能叉分。
  • Type=forking:systemd希望原始的服務進程在叉分後終止,原始進程終止時,systemd視其爲服務準備就緒。

Type=simple選項並不負責服務花多長時間啓動,systemd也不知道何時啓動該服務的依賴關係。解決這個問題的一個辦法是使用延時啓動。不過我們可以使用Type來讓服務就緒時通知systemd。

  • Type=notify:服務在就緒時向systemd發送通知(使用sd_notifiy()函數)。
  • Type=dbus:服務在就緒時向D-Bus(Desktop Bus)註冊自己

另外還有一個服務啓動類型是Type=oneshot,其中服務進程在完成任務後會徹底終止。

最後還有一個類型Type=idle,意思是在當前沒有任何激活任務的情況下,systemd纔會激活該服務。這個類型主要是用於等其他服務都啓動完成後,再啓動制定的服務,這樣可以減輕系統負載,還可以避免服務啓動過程之間的交叉。

systemd的按需和資源並行啓動

systemd的一個最主要的特性是它可以延遲啓動單元,直到它們真正被需要爲止。

  1. 爲系統服務創建一個systemd單元(單元A)
  2. 標識出單元A需要爲其服務提供的系統資源,如網絡端口、網絡套接字或者設備。
  3. 創建另一個systemd單元(單元R)來表示該資源,它有特殊的單元類型。

其運行步驟如下所示:

  1. 單元R激活的時候,systemd對其資源進行監控。
  2. systemd將阻止所有對該資源的訪問,對該資源的輸入會被緩衝
  3. systemd激活單元A
  4. 當單元A啓動的服務就緒時,其獲得對資源的控制,讀取緩衝的輸入,然後正常運行。

使用輔助的單元優化啓動

systemd在激活單元時通常會試圖簡化依賴關係和縮短啓動時間。這類似於按需啓動,其中輔
助單元代表服務單元所需的資源,不同的地方是systemd在激活輔助單元之後立即啓動服務單元。

systemd的System V兼容性

systemd中有一個特性讓其有別於其他的新一代init系統,就是對於System V兼容init腳本啓動的服務,systemd會盡量完全地進行監控。它的工作流程如下所示。

  1. systemd首先激活runlevel.target,其中N是運行級別。
  2. systemd爲/etc/rc.d中的每一個符號鏈接在/etc/init.d中標識出對應腳本。
  3. systemd將腳本名和服務單元關聯起來(例如:/etc/init.d/foo對應foo.service)
  4. systemd根據rc.d中的名稱,激活服務單元,使用參數start或者stop運行腳本。
  5. systemd嘗試關聯腳本進程和服務單元。

由於systemd根據服務單元來建立關聯,你可以使用systemctl來重啓服務和查看其狀態。不過System V兼容模式仍然按順序執行init腳本

systemd輔助程序

使用systemd的時候,你可能會注意到/lib/systemd目錄中有大量的程序,它們主要是單元的支持程序

例如,作爲systemd的一個組成部分,udevd對應的程序文件是systemd-udevd。此外,程序文件systemd-fsck是作爲systemd和fsck的“中間人”而存在。

這些程序很多都有標準系統工具程序所不具備的消息通知機制。它們通常運行標準系統工具程序,然後將執行結果通知給systemd。

System V init

Linux上的System V init實現要追溯到Linux的早期版本,它根本目的是爲了爲系統提供合理的啓動順序,支持不同的運行級別。雖然現在System V已經不太常見,不過在Red Hat EnterpriseLinux和一些路由器和電話的Linux嵌入系統中還是能夠看到System V init。

典型的System V init安裝包含兩個主要組件:一個核心配置文件和一組啓動腳本以及符號鏈接集。配置文件/etc/inittab是核心。如果你係統中有System V init的話,你可以從中看到如下內容:

id:5:initdefault:

這表示運行級別默認爲5。

inittab中的內容都有如下格式,四列內容使用分號隔開,分別是:

  • 唯一標識符(一串短字符,本例中爲id)
  • 運行級別值(一個或多個)
  • init執行的操作(本例中是將運行級別設置爲5)
  • 執行的命令(可選項)

下面一行內容告訴我們命令如何運行:

l5:5:wait:/etc/rc.d/rc 5

這行內容很重要,它觸發大部分的系統配置和服務。wait操作決定System V init何時和怎樣運行命令。進入運行級別5時運行一次/etc/rc.d/rc 5,然後一直等待命令執行完畢。rc 5命令運行/etc/rc5.d中所有以數字開頭的命令(按數字的順序)。

respawn
respawn讓init在其後的命令結束執行後,再次運行。在inittab文件中你有可能會看到以下內容

1:2345:respawn:/sbin/mingetty tty1

getty程序提供登錄提示符。上面的命令是針對第一個虛擬控制檯(/dev/tty1)的,當你按ALT-F1或者CTRL-ALT-F1時能夠看到(參考3.4.4節)。respawn在你退出系統後重新顯示登錄提示符。

ctrlaltdel

ctrlaltdel是控制當你在虛擬控制檯中按CTRL-ALT-DEL鍵時系統採取的操作。在大部分系統中,這是重啓命令,它執行shutdown命令。

sysinit

sysinit是init在啓動過程中,且在進入運行級別之前執行的第一個操作。

System V init啓動命令順序

l5:5:wait:/etc/rc.d/rc 5

它只是一行簡單的指令,但實際觸發了很多其他程序。rc是運行命令(run command)的簡寫,我們會在許多腳本、程序和服務中使用到它。那麼運行的命令在哪裏呢?

該行中的5代表運行級別5。運行的命令多半是在/etc/rc.d/rc5.d或者/etc/rc5.d中。(運行級別1使用rc1.d,運行級別2使用rc2.d,以此類推。)你可能會在rc5.d目錄下找到以下內容:

S10sysklogd S20ppp S99gpm
S12kerneld S25netstd_nfs S99httpd
S15netstd_init S30netstd_misc S99rmnologin
S18netbase S45pcmcia S99sshd
S20acct S89atd
S20logoutd S89cron

rc 5命令通過執行下面的命令來運行rc5.d目錄下的程序:

S10sysklogd start
S12kerneld start
S15netstd_init start
S18netbase start
--snip--
S99sshd start

請注意每一行中的start參數。命令名中的大寫S表示命令應該在start模式中運行,數字009900\sim 99決定了rc啓動命令的順序。rc*.d命令通常是命令行腳本,用來啓動/sbin或者/usr/sbin中的程序。

System V init 鏈接池

rc*.d目錄實際上包含的是符號鏈接,指向init.d目錄中的文件。如果想運行、添加、刪除或者更改rc*.d目錄中的服務,你需要了解這些符號鏈接。

lrwxrwxrwx . . . S10sysklogd -> ../init.d/sysklogd
lrwxrwxrwx . . . S12kerneld -> ../init.d/kerneld
lrwxrwxrwx . . . S15netstd_init -> ../init.d/netstd_init
lrwxrwxrwx . . . S18netbase -> ../init.d/netbase
--snip--
lrwxrwxrwx . . . S99httpd -> ../init.d/httpd
--snip--

子目錄中所包含的大量符號鏈接,我們稱之爲鏈接池(Link Farm)。有了這些鏈接,Linux可以對不同的運行級別使用相同的啓動腳本。雖然不需要嚴格遵循,但這種方法確實更簡潔。

啓動和停止服務

如果要手動啓動和停止服務,可以使用init.d目錄中的腳本。比如我們可以運行init.d/httpd start來啓動httpd Web服務。類似地,使用stop參數(如httpd stop)來關閉服務。

更改啓動順序

在System V init中更改啓動順序是通過更改鏈接池來完成的。最常見的更改是禁止init.d目錄中的某個命令在某個特定的運行級別中運行。在進行此操作時需謹慎。因爲假如你刪除了某個rc*.d目錄中的一個符號鏈接,將來你想恢復它的時候,你可能已經忘記了它的鏈接名。所以一個比較好的辦法是在鏈接名前加下劃線(_),如

mv S99httpd _S99httpd

這一指令使得rc忽略_S99httpd,因爲文件名不以S或K開頭,同時又保留了原始的鏈接名。

如果要添加服務,我們可以在init.d目錄中創建一個腳本文件,然後在相應的rc*.d目錄中創建指向它的符號鏈接。最簡單的辦法是在init.d目錄中複製和修改你熟悉的腳本。

在添加服務的時候,需要爲其設置適當的啓動順序。如果服務啓動過早,可能會導致失敗,因爲它依賴的其他服務可能還沒有就緒。對於那些不重要的服務,大多數系統管理員會爲它們設置90以後的序號,以便讓系統服務首先啓動。

run-parts

run-parts默認運行目錄中的所有可執行程序,也有可選項用來指定執行或忽略某些特定程序。

System V init 控制

你可以使用telinit來操縱System V init

telinit 3

切換到運行級別3。運行級別切換時,init會試圖終止所有新運行級別的inittab文件中沒有包括的進程,所以需要
小心操作。

關閉系統

init控制系統的啓動和關閉。關閉系統的命令在所有init版本中都是一樣的。關閉Linux系統最好的方式是使用shutdown命令。

shutdown命令有兩種使用方法,一是使用-h可選項關閉系統,並且使其一直保持關閉狀態。下面的命令能夠立即關閉系統:

shutdown -h now

下面的命令在10分鐘後重啓系統

shutdown -r +10

關閉過程大致如下所示:

  1. init通知所有進程安全關閉
  2. 如果某個進行沒有及時響應,init會先用TERM信號嘗試終止它。
  3. 如果TERM信號無效,init會使用kill信號。
  4. 鎖定系統文件,並且進行其他關閉準備工作。
  5. 系統卸載root以外的所有文件系統。
  6. 系統以只讀模式重新掛載root文件系統
  7. 系統將所有緩衝區的數據通過sync程序寫到文件系統。
  8. 最後一步是使用reboot(2)系統調用通知內核重啓或者停止。這一步驟是由init或者其他輔助程序如reboot、halt或者poweroff來完成。

initramfs

Linux啓動過程很簡單。但是其中的一個組件是initramfs,或稱爲初始RAM文件系統。可以把它看作是一個用戶空間的楔子,在用戶空間啓動前出現。

Linux內核從磁盤讀取數據時不直接與BIOS和EFI接口通信。爲了掛載root文件系統,它需要底層的驅動程序支持。如果root文件系統存放在一個連接到第三方控制器的磁盤陣列(RAID)上,內核首先就需要這個控制器的驅動程序。因爲存儲控制器的驅動程序種類繁多,內核不可能把它們都包含進來,所以很多驅動程序都以可加載模塊的方式出現。可加載模塊是以文件形式來存放,如果內核一開始沒有掛載文件系統的話,它就無法加載需要的這些驅動模塊了。

解決的辦法是將一小部分內核驅動模塊和工具打包爲一個文檔。引導裝載程序在內核運行前將該文檔載入內存。內核在啓動時將文檔內容讀入一個臨時的initramfs,然後掛載到/上,將用戶模式切換給initramfs上的init,然後使用initramfs中的工具讓內核加載root文件系統需要的驅動模塊。最後,這些工具掛載真正的root文件系統,啓動真正的init。

initramfs的一個始終未變的特性是你可以在不需要時跳過它。也就是說,如果內核已經有了所有它需要的用來掛載根文件系統的驅動程序,你就可以在你的引導裝載程序配置中跳過initramfs。跳過該過程能夠縮短幾秒鐘的啓動時間。你可以自己嘗試在GRUB菜單編輯器中刪除initrd行。(最好不要使用GRUB配置文件來做實驗,一旦出錯很難恢復。)目前來說,initramfs還是需要的,因爲大多數的Linux內核並不包含諸如通過UUID掛載這些特性。

創建initramfs的過程很複雜,不過通常我們不需要自己動手。有很多工具可以供我們使用,Linux系統中通常都會自帶,如dracut和mkinitramfs是最爲常用的兩個。

緊急啓動和單用戶模式

當系統出現問題時,首先採取的措施通常是使用系統安裝映像來啓動系統,或者使用SystemRescueCd這樣可以保存到移動存儲設備上的恢復映像。系統修復的任務大致包括以下幾方面:

  • 系統崩潰後,檢查文件系統
  • 重置系統管理員密碼
  • 修復關鍵的系統文件,如/etc/fstab和/etc/passwd
  • 系統崩潰後,藉助備份數據恢復系統

除上述措施外,快速啓動的另一個可選途徑是啓用單用戶模式。它將系統啓動到root命令行,而不是完整啓動所有的服務。

在System V init中,運行級別1通常是單用戶模式。你也可以在引導裝載程序中使用-s參數來進入此模式,只不過可能需要輸入root密碼。

單用戶模式的最大問題是它提供的服務有限,如網絡、圖形界面和終端通常都不可用。所以我們在系統恢復時通常優先考慮系統安裝映像。

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