第 3 章 系統初始化

目录

3.1. 啓動過程概述
3.1.1. 第一階段:BIOS
3.1.2. 第二階段:引載加載程序
3.1.3. 第三階段:迷你 Debian 系統
3.1.4. 第四階段:常規 Debian 系統
3.2. Systemd 初始化
3.2.1. 主機名
3.2.2. 檔案系統
3.2.3. 網路介面初始化
3.2.4. 核心訊息
3.2.5. 系統訊息
3.2.6. systemd 下的系統管理
3.2.7. 定製 systemd
3.3. udev 系統
3.3.1. 核心模組初始化

作爲系統管理員,粗略地瞭解 Debian 系統的啓動和配置方式是明智的。儘管準確的細節在安裝的軟件包及對應的文檔中,但這些知識對我們大多數人來說都是必須掌握的。

筆者基於自己和其他人的過往及現在的知識,盡己所能地提供關於 Debian 系統的知識要點及其配置的快速概覽作爲讀者的參考。由於 Debian 系統在不斷地更新中,系統的狀況可能已經有所變化。在對系統做任何修改之前,請參考各個軟件包的最新文檔。

[提示] 提示

bootup(7) 介紹了基於 systemd 的系統啟動流程。(近期的 Debian)

[提示] 提示

boot(7) 介紹了基於 UNIX System V Release 4 的系統啟動流程。(舊版的 Debian)

計算機系統從上電事件到能爲用戶提供完整的操作系統(OS)功能爲止,需要經歷幾個階段的啓動過程

爲簡便起見,筆者將討論範圍限定在具有默認安裝的典型 PC 平臺上。

典型的啓動過程像是一個四級的火箭。每一級火箭將系統控制權交給下一級。

當然,這些階段可以有不同的配置。比如,你編譯了自己的內核,則可能會跳過迷你 Debian 系統的步驟。因此,在讀者親自確認之前,請勿假定自己系統的情況也是如此。

[注意] 注意

對於 SUN 或 Macintosh 系統等非傳統 PC 平臺來說,ROM 上的 BIOS 及磁盤上的分區可能大不相同(第 9.5.2 节 “硬碟分割槽配置”)。對於這種情況,請另尋對應平臺相關的文檔。

BIOS 是啓動過程的第一階段,在上電事件後開始。CPU 的程序計數器在上電事件後被初始化爲一個特定的內存地址,駐留在只讀存儲器(ROM)中的 BIOS 就是從這個特定的內存地址開始執行。

BIOS 執行硬件的基本初始化(POST: 上電自檢)並將系統控制權交給你指定的下一步驟。BIOS 通常和硬件一同提供。

BIOS 啓動屏幕通常指示了進入 BIOS 配置界面所需的按鍵。流行的按鍵是 F1、F2、F10、Esc、Ins 和 Del 鍵。假如你的啓動屏幕被一個漂亮的圖形界面隱藏,你可以按下某些按鍵(比如 ESC)取消隱藏。這些按鍵高度依賴於硬件。

硬件位置和 BIOS 啓動的代碼的優先級可以在 BIOS 配置界面中選擇。通常,在已選擇的設備(硬盤、軟件、CD-ROM……)中,最先找到的設備的最開始的幾個扇區將被加載到內存,並執行其中的初始化代碼。初始化代碼可以是以下任意一種。

  • 引導加載代碼

  • 類似 FreeDOS 這樣的過濾型操作系統的內核代碼

  • 能夠加載到如此小的空間中的目標操作系統的內核代碼

通常,系統從主硬件的特定分區中引導。傳統 PC 硬盤的最開始兩個扇區中包含了主引導記錄(MBR)。在 MBR 的末尾記錄了磁盤分區信息及引導選擇。BIOS 中執行的首段引導加載代碼佔據了 MBR 的其餘部分。

引導加載程序是啓動過程的第二階段,由 BIOS 啓動。引導加載程序將系統內核映像和 initrd 映像加載到內存並將控制權交給它們。initrd 映像是根文件系統映像,其支持程度依賴於所使用的引導加載程序。

Debian 系統通常使用 Linux 核心作為其預設的系統核心。當前 2.6/3.x 版本 Linux 核心的 initrd 映象從技術上說是 initramfs(初始化 RAM 檔案系統)映象。基本的 initrd 映象是 root 檔案系統中各個檔案使用 cpio 壓縮得到的。 核心可以在啟動流程中非常早的階段,在載入基本的 initrd 映象之前即更新微碼。 以未壓縮 cpio 格式儲存微碼二進位制檔案的 initrd 映象和基本 initrd 映象兩部分可以聯合組成一個 initrd 映象,從而幫助實現上述功能。

[提示] 提示

您可以使用 lsinitramfs(8)unmkinitramfs(8) 這兩個工具檢查 initrd 映象檔案的內容,它們由 initramfs-tools-core軟體包提供。 另見 https://wiki.debian.org/initramfs 以瞭解更多資訊。

Debian 系統默認將 PC 平臺的 GRUB 引導加載程序的第一階段代碼安裝在 MBR 中。可用的引導加載程序和配置選項如下。


[警告] 警告

假如沒有從 grub-rescue-pc 軟體包中的映像製作出來的可引導修復盤(U盤、CD 或軟盤),請勿玩弄引導載入程式。即使硬碟上沒有可正常工作的引導載入程式,可引導修復盤也能引導你的系統。

傳統 GRUB 的菜單配置文件位於 /boot/grub/menu.lst。例如,文件中有如下的配置條目。

title           Debian GNU/Linux
root            (hd0,2)
kernel          /vmlinuz root=/dev/hda3 ro
initrd          /initrd.img

GRUB 第 2 版的菜單配置文件位於 /boot/grub/grub.cfg。此文件由 /usr/sbin/update-grub 根據 "/etc/grub.d/*" 中的模板及 "/etc/default/grub" 中的設置自動生成。例如,文件中有如下的配置條目。

menuentry "Debian GNU/Linux" {
        set root=(hd0,3)
        linux /vmlinuz root=/dev/hda3
        initrd /initrd.img
}

這些示例中,GRUB 參數的含義如下。


[注意] 注意

傳統 GRUB 使用的分區號爲 Linux 內核及各種實用工具使用的分區號減 1。GRUB 第 2 版修復了這個問題。

[提示] 提示

在標識一個塊設備時,可能需要使用 UUID(參見第 9.5.3 节 “使用 UUID 訪問分割槽”)而不是類似 "/dev/hda3" 這樣的文件名,例如 "root=UUID=81b289d5-4341-4003-9602-e254a17ac232 ro"。

[提示] 提示

如果使用了 GRUB,內核的啓動參數可以在 /boot/grub/grub.cfg 裏面設置。在 Debian 系統裏,你不應該直接編輯 /boot/grub/grub.cfg。你可以通過編輯 /etc/default/grub 文件中 GRUB_CMDLINE_LINUX_DEFAULT 的值並運行 update-grub(8) 來更新 /boot/grub/grub.cfg

[提示] 提示

通過使用鏈式引導技術,你可以在一個引導裝載程序中啓動另一個引導裝載程序。

參見 “info grub” 及 grub-install(8)

迷你 Debian 系統是啓動流程的第三階段,由引導加載程序啓動。它會在內存中運行系統內核和根文件系統。這是啓動流程的一個可選準備階段。

[注意] 注意

“迷你 Debian 系統”是筆者自創的術語,用於在本文檔中描述啓動流程的第三個階段。這個系統通常被稱爲 initrd 或 initramfs 系統。內存中類似的系統在 Debian 安裝程序中使用。

/init 程式是記憶體中的根檔案系統上執行的第一個程式。這個程式在使用者空間把核心初始化,並把控制權交給下一階段。迷你 Debian 系統能夠在主引導流程之前新增核心模組或以加密形式掛載根檔案系統,使引導流程更加靈活。

  • 如果 initramfs 是由 initramfs-tools 建立,則"/init" 程式是一個 shell 指令碼程式。

    • 通過給內核添加 “break=init" 等啓動參數,你可以中斷這部分啓動流程以獲取 root shell。更多中斷條件請參見 ”/init“ 腳本。這個 shell 環境已足夠成熟,你可通過它很好地檢查機器的硬件。

    • 迷你 Debian 系統中可用的命令是精簡過的,且主要由一個稱爲 busybox(1) 的 GNU 工具提供。

  • 如果 initramfs 是由 dracut 建立,則 "/init" 程式是一個二進位制 systemd 程式。

    • 迷你 Debian 系統中可用的命令是一個精簡過的 systemd(1) 環境。

[小心] 小心

當在一個只讀的根文件系統上時,使用 mount 命令需要添加 -n 選項。

常規 Debian 系統是啓動流程的第四階段,由迷你 Debian 系統啓動。迷你 Debian 系統的內核在此環境下繼續運行。根文件系統將由內存切換到實際的硬盤文件系統上。

init 程序是系統執行的第一個程序(PID=1),它啓動其它各種程序以完成主引導流程。init 程序的默認路徑是 ”/sbin/init“,但可通過內核啓動參數修改,例如 ”init=/path/to/init_program"。

默認的 init 程序一直在變化中:

  • squeeze 之前的 Debian,使用簡單的 SysV 風格的 init。

  • wheezy 版本的 Debian 對 SysV 風格的 init 做了改進:使用 LSB 頭將啓動步驟排序,同時並行執行啓動腳本。

  • jessie版本的 Debian 將默認 init 切換成 systemd,以使用事件驅動和並行初始化。

[提示] 提示

你的系統中實際使用的 init 命令可以使用 “ps --pid 1 -f” 命令確認。

[提示] 提示

在 Debian jessie 版本後,"/sbin/init" 是一個到 "/lib/systemd/systemd" 的符號連結。


[提示] 提示

有關啟動流程加速的最新資訊,請參見 Debian 維基:啟動流程加速詞條。

本節描述系統是怎樣通過 PID=1systemd(1) 程式來啟動(即初始化程序)。

systemd 初始化程序基於單元配置檔案 (參見 systemd.unit(5)) 來並行派生程序,這些單元配置檔案使用宣告樣式來書寫,代替之前的類 SysV 的過程樣式。 這些單元配置檔案從下面的一系列路徑來載入 (參見 systemd-system.conf(5)) :

  • "/lib/systemd/system": OS 預設配置檔案

  • "/etc/systemd/system": 系統管理員的配置檔案,它將忽略作業系統預設的配置檔案

  • "/run/systemd/system": 執行時產生的配置檔案,它將忽略安裝的配置檔案

他們的相互依賴關係通過"Wants=", "Requires=", "Before=", "After=", … 等指示來配置,(參見 systemd.unit(5) 裡的 "MAPPING OF UNIT PROPERTIES TO THEIR INVERSES")。 資源控制也是被定義 (參見 systemd.resource-control(5)).

根據單元配置檔案的字尾來區分它們的型別:

  • *.service 描述由 systemd 控制和監管的程序.參見 systemd.service(5).

  • *.device 描述在 sysfs(5) 裡面作為 udev(7) 裝置樹展示的裝置。參見 systemd.device(5).

  • *.mount 描述由 systemd 控制和監管的檔案系統掛載點。參見 systemd.mount(5).

  • *.automount 描述由 systemd 控制和監管的檔案系統自動掛載點。參見 systemd.automount(5).

  • *.swap 描述由 systemd 控制和監管的 swap 檔案或裝置。參見 systemd.swap(5).

  • *.path 描述被 systemd 監控的路徑,用於基於路徑的活動。參見 systemd.path(5).

  • *.socket 描述被 systemd 控制和監管的套接字,用於基於套接字的活動。參見 systemd.socket(5).

  • *.timer 描述被 systemd 控制和監管的計時器,用於基於時間的活動。參見 systemd.timer(5).

  • *.slice 管理 cgroups(7) 的資源。參見 systemd.slice(5).

  • *.scope 使用 systemd 的匯流排介面來程式化的建立,用以管理一系列系統程序。 參見 systemd.scope(5).

  • *.target 把其它單元配置檔案分組,在啟動的時候,來建立同步點。參見systemd.target(5).

系統啟動時(即,init),systemd 程序會嘗試啟動"/lib/systemd/system/default.target(通常是到"graphical.target"的符號連結)。首先,一些特殊的 target 單元(參見 systemd.special(7)),比如 "local-fs.target"、"swap.target"和"cryptsetup.target"會被引入以掛載檔案系統。之後,其它 target 單元也會根據單元依賴關係而被引入。詳細情況,請閱讀 bootup(7)

systemd 提供向後相容的功能。在 "/etc/init.d/rc[0123456S].d/[KS]<name>" 裡面的 SysV 風格的啟動指令碼仍然會被分析;telinit(8) 會被轉換為 systemd 的單元活動請求。

[小心] 小心

模擬的執行級別 2 到 4 全部被符號連結到了相同的“multi-user.target”。

硬碟和網路檔案系統的掛載選項可以在 "/etc/fstab" 中設定,參見 fstab(5)第 9.5.7 节 “通過掛載選項優化檔案系統”

加密檔案系統的配置設定在“/etc/crypttab”中。參見 crypttab(5)

軟 RAID 的配置 mdadm(8) 設定在 "/etc/mdadm/mdadm.conf". 參見 mdadm.conf(5).

[警告] 警告

每次啟動的時候,在掛載了所有檔案系統以後,"/tmp", "/var/lock", 和 "/var/run" 中的臨時檔案會被清空。

systemd 不僅僅提供系統初始化,還提供通用的系統管理功能。比如說日誌記錄,登入管理,時間管理,網路管理等等。

systemd(1) 通過幾個命令來管理:

  • systemctl(1) 命令控制 systemd 的系統和服務管理器(命令列),

  • systemsdm(1) 命令控制 systemd 的系統和服務管理器(圖形介面),

  • journalctl(1) 命令查詢 systemd 日誌,

  • loginctl(1) 命令控制 systemd 登入管理器,

  • systemd-analyze(1) 分析系統啟動效能。

這裡有一個典型的 systemd 管理命令片段列表。確切含義,請閱讀相關 man 手冊頁。

表 3.5. 典型的systemd 管理命令片段列表

操作 型別 命令片段
用於服務管理的圖形介面 圖形介面 "systemadm" (systemd-ui 軟體包)
列出所有 target 單元配置 單元 "systemctl list-units --type=target"
列出所有 service 單元配置 單元 "systemctl list-units --type=service"
列出所有單元配置型別 單元 "systemctl list-units --type=help"
列出記憶體中所有 socket 單元 單元 "systemctl list-sockets"
列出記憶體中所有 timer 單元 單元 "systemctl list-timers"
啟動 "$unit" 單元 "systemctl start $unit"
停止 "$unit" 單元 "systemctl stop $unit"
重新載入服務相關的配置 單元 "systemctl reload $unit"
停止和啟動所有 "$unit" 單元 "systemctl restart $unit"
啟動 "$unit" 並停止所有其它的 單元 "systemctl isolate $unit"
轉換到 "圖形" (圖形介面系統) 單元 "systemctl isolate graphical"
轉換到 "多使用者" (命令列系統) 單元 "systemctl isolate multi-user"
轉換到 "應急模式" (單使用者命令列系統) 單元 "systemctl isolate rescue"
向"$unit"傳送殺死訊號 單元 "systemctl kill $unit"
檢查"$unit"服務是否是活動的 單元 "systemctl is-active $unit"
檢查"$unit"服務是否是失敗的 單元 "systemctl is-failed $unit"
檢查"$unit|$PID|device"的狀態 單元 "systemctl status $unit|$PID|$device"
顯示"$unit|$job"的屬性 單元 "systemctl show $unit|$job"
重設失敗的"$unit" 單元 "systemctl reset-failed $unit"
列出所有單元服務的依賴性 單元 "systemctl list-dependencies --all"
列出安裝在系統上的單元檔案 單元檔案 "systemctl list-unit-files"
啟用 "$unit" (增加符號連結) 單元檔案 "systemctl enable $unit"
禁用 "$unit" (刪除符號連結) 單元檔案 "systemctl disable $unit"
取消遮掩 "$unit" (刪除到 "/dev/null" 的符號連結) 單元檔案 "systemctl unmask $unit"
遮掩 "$unit" (增加到 "/dev/null" 的符號連結) 單元檔案 "systemctl mask $unit"
獲取預設的 target 設定 單元檔案 "systemctl get-default"
設定預設 target 為"graphical" (圖形系統) 單元檔案 "systemctl set-default graphical"
設定預設的 target 為"multi-user" (命令列系統) 單元檔案 "systemctl set-default multi-user"
顯示工作環境變數 環境變數 "systemctl show-environment"
設定環境變數 "variable" 的值為 "value" 環境變數 "systemctl set-environment variable=value"
取消環境變數 "variable" 的設定 環境變數 "systemctl unset-environment variable"
重新載入所有單元檔案和後臺守護程序(daemon) 生命週期 "systemctl daemon-reload"
關閉系統 系統 "systemctl poweroff"
關閉和重啟系統 系統 "systemctl reboot"
掛起系統 系統 "systemctl suspend"
休眠系統 系統 "systemctl hibernate"
檢視 "$unit" 的工作日誌 日誌 "journalctl -u $unit"
檢視 "$unit"的工作日誌 ("tail -f" 式樣) 日誌 "journalctl -u $unit -f"
顯示每一個初始化步驟所消耗的時間 分析 "systemd-analyze time"
列出所有單元的初始化時間 分析 "systemd-analyze blame"
載入"$unit"檔案並檢測錯誤 分析 "systemd-analyze verify $unit"
跟蹤 cgroups(7) 的啟動過程 Cgroup "systemd-cgls"
跟蹤 cgroups(7) 的啟動過程 Cgroup "ps xawf -eo pid,user,cgroup,args"
跟蹤 cgroups(7) 的啟動過程 Cgroup 讀取 "/sys/fs/cgroup/systemd/" 下的 sysfs

這裡, 上面例子中的"$unit",可以是一個單元名(字尾.service.target 是可選的),或者,在很多情況下,也可以是匹配的多個單元 (shell 式樣的全域性萬用字元"*", "?", "[]",通過使用 fnmatch(3) ,來匹配目前在記憶體中的所有單元的基本名稱).

上面列子的系統狀態改變命令,通常是通過"sudo"來處理,用以獲得需要的系統管理許可權。

"systemctl status $unit|$PID|$device" 的輸出使用有顏色的點("●")來概述單元狀態,讓人看一眼就知道。

  • 白色的 "●" 表示一個 "不活動"或"變為不活動中"的狀態。

  • 紅色的 "●"表示“失敗”或者“錯誤”狀態。

  • 綠色"●"表示“活動”、“重新載入中”或“啟用中”狀態。

使用預設安裝,通過 systemd 啟動的過程中,在 network.target 啟動後,很多網路服務 (參見 第 6 章 網路應用)作為後臺守護程序(daemon)啟動。 "sshd" 也不列外。讓我們修改為按需啟動"sshd" 作為一個定製化的例子。

首先,禁用系統安裝的服務單元。

 $ sudo systemctl stop sshd.service
 $ sudo systemctl mask sshd.service

傳統 Unix 服務的按需套接字啟用(on-demand socket activation)系統由 indetd 超級服務來提供。在 systemd 下, 相同功能能夠通過增加*.socket*.service 單元配置檔案來啟用。

sshd.socket 用來定義一個監聽的套接字

[Unit]
Description=SSH Socket for Per-Connection Servers

[Socket]
ListenStream=22
Accept=yes

[Install]
WantedBy=sockets.target

sshd@.service 作為 sshd.socket 匹配的服務檔案

[Unit]
Description=SSH Per-Connection Server

[Service]
ExecStart=-/usr/sbin/sshd -i
StandardInput=socket

然後重新載入。

 $ sudo systemctl daemon-reload

Linux 核心 2.6 和更新的核心,udev 系統 提供了自動硬體發現和初始化機制。(參見 udev(7)).在核心發現每個裝置的基礎上,udev 系統使用從 sysfs 檔案系統 (參見 第 1.2.12 节 “procfs 和 sysfs”)的資訊啟動一個使用者程序,使用 modprobe(8) 程式 (參見 第 3.3.1 节 “核心模組初始化”)載入支援它所要求的核心模組, 建立相應的裝置節點。

[提示] 提示

如果由於某些理由,"/lib/modules/<kernel-version>/modules.dep"沒有被 depmod(8) 正常生成,模組可能不會被 udev 系統按期望的方式載入。執行"depmod -a" 來修復它。

裝置節點的名字,可以通過"/etc/udev/rules.d/"裡的 udev 檔案來配置.當前預設的規則傾向建立動態生成的名字,除了光碟機和網路裝置外,會生成非靜態的裝置名。通過新增和光碟機、網路裝置類似的個性化規則,你也可以為 USB 盤之類的其它裝置,生成靜態裝置名。 參見 "Writing udev rules" 或 "/usr/share/doc/udev/writing_udev_rules/index.html".

由於 udev 系統是一個正在變化的事物,我在其它文件進行了詳細描述,在這裡只提供了最少的資訊。

[提示] 提示

"/etc/fstab"裡面的掛載規則,裝置節點不必需是靜態的。你能夠使用 UUID 來掛載裝置,來代替"/dev/sda"之類的裝置名. 參見 第 9.5.3 节 “使用 UUID 訪問分割槽”.

通過 modprobe(8) 程式新增和刪除核心模組,使我們能夠從使用者程序來配置正在執行的 Linux 核心。udev 系統(參見 第 3.3 节 “udev 系統”)自動化它的呼叫來幫助核心模組初始化。

下面的非硬體模組和特殊的硬體驅動模組,需要被預先載入,把它們在"/etc/modules"檔案裡列出 (參見 modules(5)).

modprobe(8) 程式的配置檔案是按 modprobe.conf(5)的說明放在"/etc/modprobes.d/" 目錄下,(如果你想避免自動載入某些核心模組,考慮把它們作為黑名單放在"/etc/modprobes.d/blacklist" 檔案裡.)

"/lib/modules/<version>/modules.dep" 檔案由 depmod(8) 程式生成,它描述了 modprobe(8) 程式使用的模組依賴性.

[注意] 注意

如果你在啟動時出現模組載入問題,或者 modprobe(8)時出現模組載入問題, "depmod -a" 可以通過重構"modules.dep"來解決這些問題。

modinfo(8) 程式顯示 Linux 核心模組資訊。

lsmod(8) 程式以好看的格式展示"/proc/modules"的內容,顯示當前核心載入了哪些模組。

[提示] 提示

你能夠精確識別你係統上的硬體。 參見第 9.4.3 节 “硬體識別”.

[提示] 提示

你可以在啟動時配置硬體來啟用期望的硬體特徵。參見 第 9.4.4 节 “硬體配置”.

[提示] 提示

你可以重新編譯核心來增加你的特殊裝置的支援。參見 第 9.9 节 “核心”.