2021-05-12 14:32:11
關於 systemd 的初步理解
從 init 系統說起
Linux 作業系統的啟動首先從 BIOS 開始,接下來進入 boot loader,由 bootloader 載入核心,進行核心初始化。核心初始化的最後一步就是啟動 PID 為 1 的 init 進程。這個進程是系統的第一個進程。它負責產生其他所有的使用者進程。init 進程以守護行程(也就是服務)的方式存在,是所有其他進程的祖先。init 進程非常獨特,能夠完成其他進程無法完成的任務。
init 系統能夠定義、管理和控制 init 進程的行為。它負責組織和執行許多獨立的或相關的初始化工作(因此被稱為 init 系統),從而讓計算機系統進入某種使用者預定義的執行模式,比如命令列模式或圖形介面模式 。
對於一個作業系統而言,僅僅將核心執行起來是毫無實際用途的,必須由 init 系統將作業系統初始化為可操作的狀態。比如啟動 shell 後,便有了人機互動,這樣就可以讓計算機執行一些程式完成有實際意義的任務。或者啟動 X 圖形系統以便提供更佳的人機介面,更加高效的完成任務。這裡,字元介面的 shell 或者 X 系統都是一種預設的執行模式。
隨著計算機系統軟硬體的發展,init 系統也在不斷的發展變化之中。大體上的演進路線為 sysvinit -> upstart -> systemd。雖然本文的目的是要介紹 systemd,但是筆者覺得如果能從歷史發展的角度觀察 init 系統的演進,將會幫助我們更好的理解、使用 systemd。
sysvinit
sysvinit 就是 System V 風格的 init 系統,顧名思義,它源於 System V 系列的 UNIX。最初的 linux 發行版幾乎都是採用 sysvinit 作為 init 系統。sysvinit 用術語 runlevel 來定義 "預訂的執行模式"。比如 runlevel 3 是命令列模式,runlevel 5 是圖形介面模式,runlevel 0 是關機,runlevel 6 是重新啟動。sysvinit 會按照下面的順序按部就班的初始化系統:
- 啟用 udev 和 selinux
- 設定定義在 /etc/sysctl.conf 中的核心引數
- 設定系統時鐘
- 載入 keymaps
- 啟用交換分割區
- 設定主機名(hostname)
- 根分割區檢查和 remount
- 啟用 RAID 和 LVM 裝置
- 開啟磁碟配額
- 檢查並掛載所有檔案系統
- 清除過期的 locks 和 PID 檔案
- 最後找到指定 runlevel 下的指令碼並執行,其實就是啟動服務。
除了負責初始化系統,sysvinit 還要負責關閉系統,主要是在系統關閉是為了保證資料的一致性,需要小心地按照順序進行任務的結束和清理工作。另外,sysvinit 還提供了很多管理和控制系統的命令,比如 halt、init、mesg、shutdown、reboot 等等。
sysvinit 的優點是概念簡單。特別是服務(service)的設定,只需要把啟動/停止服務的指令碼連結接到合適的目錄就可以了。
sysvinit 的另一個重要優點是確定的執行順序,指令碼嚴格按照順序執行(sysvinit 靠指令碼來初始化系統),一個執行完畢再執行下一個,這非常有益於錯誤排查。
同時,完全順序執行任務也是 sysvinit 最致命的缺陷。如果 linux 系統只用於伺服器系統,那麼漫長的啟動過程可能並不是什麼問題,畢竟我們是不會經常重新啟動伺服器的。但是現在 linux 被越來越多的用在了桌面系統中,漫長的啟動過程對桌面使用者來說是不能接受的。除了啟動慢,sysvinit 還有一些其它的缺陷,比如不能很好的處理隨插即用的裝置,對網路共用磁碟的掛載也存在一定的問題,於是 init 系統開始了它的進化之旅。
upstart
由於 sysvinit 系統的種種弊端,Ubuntu 的開發人員決定重新設計和開發一個全新的 init 系統,即 upstart 。upstart 是第一個被廣泛應用的新一代 init 系統。
upstart 基於事件機制,比如 U 盤插入 USB 介面後,udev 得到核心通知,發現該裝置,這就是一個新的事件。upstart 在感知到該事件之後觸發相應的等待任務,比如處理 /etc/fstab 中存在的掛載點。採用這種事件驅動的模式,upstart 完美地解決了隨插即用裝置帶來的新問題。採用事件驅動機制也帶來了一些其它有益的變化,比如加快了系統啟動時間。sysvinit 執行時是同步阻塞的。一個指令碼執行的時候,後續指令碼必須等待。這意味著所有的初始化步驟都是序列執行的,而實際上很多服務彼此並不相關,完全可以並行啟動,從而減小系統的啟動時間。
upstart 的特點
upstart 解決了之前提到的 sysvinit 的缺點。採用事件驅動模型的 upstart 可以:
- 更快地啟動系統
- 當新硬體被發現時動態啟動服務
- 硬體被拔除時動態停止服務
這些特點使得 upstart 可以很好地應用在桌面或者便攜式系統中,處理這些系統中的動態硬體插拔特性。
主角 systemd 登場
systemd 是 linux 系統中最新的初始化系統(init),它主要的設計目標是克服 sysvinit 固有的缺點,提高系統的啟動速度。systemd 和 ubuntu 的 upstart 是競爭對手,但是時至今日 ubuntu 也採用了 systemd,所以 systemd 在競爭中勝出,大有一統天下的趨勢。其實,systemd 的很多概念都來源於蘋果 Mac OS 作業系統上的 launchd。
systemd 的優點是功能強大,使用方便,缺點是體系龐大,非常複雜,下圖展示了 systemd 的架構(此圖來自網際網路):
systemd 能夠在與 upstart 的競爭中勝出自然有很多過人之處,接下來讓我們介紹一些 systemd 的主要優點。
相容性
systemd 提供了和 sysvinit 相容的特性。系統中已經存在的服務和進程無需修改。這降低了系統向 systemd 遷移的成本,使得 systemd 替換現有初始化系統成為可能。
啟動速度
systemd 提供了比 upstart 更激進的併行啟動能力,採用了 socket / D-Bus activation 等技術啟動服務。一個顯而易見的結果就是:更快的啟動速度。為了減少系統啟動時間,systemd 的目標是:
- 盡可能啟動更少的進程
- 盡可能將更多進程並行啟動
同樣地,upstart 也試圖實現這兩個目標。下圖展示了 upstart 相對於 sysvinit 在並行啟動這個方面的改進(此圖來自網際網路):
upstart 增加了系統啟動的並行性,從而提高了系統啟動速度。但是在 upstart 中,有依賴關係的服務還是必須先後啟動。比如任務 A,B,(C,D)因為存在依賴關係,所以在這個區域性,還是序列執行。
systemd 能夠更進一步提高並行性,即便對於那些 upstart 認為存在相互依賴而必須序列的服務,比如 Avahi 和 D-Bus 也可以並行啟動。從而實現如下圖所示的並行啟動過程(此圖來自網際網路):
在 systemd 中,所有的任務都同時並行執行,總的啟動時間被進一步降低為 T1。可見 systemd 比 upstart 更進一步提高了並行啟動能力,極大地加速了系統啟動時間。
systemd 提供按需啟動能力
當 sysvinit 系統初始化的時候,它會將所有可能用到的後台服務進程全部啟動執行。並且系統必須等待所有的服務都啟動就緒之後,才允許使用者登入。這種做法有兩個缺點:首先是啟動時間過長,其次是系統資源浪費。
某些服務很可能在很長一段時間內,甚至整個伺服器執行期間都沒有被使用過。比如 CUPS,列印服務在多數伺服器上很少被真正使用到。您可能沒有想到,在很多伺服器上 SSHD 也是很少被真正存取到的。花費在啟動這些服務上的時間是不必要的;同樣,花費在這些服務上的系統資源也是一種浪費。
systemd 可以提供按需啟動的能力,只有在某個服務被真正請求的時候才啟動它。當該服務結束,systemd 可以關閉它,等待下次需要時再次啟動它。
這有點類似於以前系統中的 inetd,並且有很多文章介紹如何把過去 inetd 管理的服務遷移到 systemd。
採用 linux 的 cgroups 跟蹤和管理進程的生命週期
systemd 利用了 Linux 核心的特性即 cgroups 來完成跟蹤的任務。當停止服務時,通過查詢 cgroups ,systemd 可以確保找到所有的相關進程,從而乾淨地停止服務。
cgroups 已經出現了很久,它主要用來實現系統資源配額管理。cgroups 提供了類似檔案系統的介面,使用方便。當進程建立子進程時,子進程會繼承父進程的 cgroups 。因此無論服務如何啟動新的子進程,所有的這些相關進程都會屬於同一個 cgroups ,systemd 只需要簡單地遍歷指定的 cgroups 即可正確地找到所有的相關進程,將它們一一停止即可。
啟動掛載點和自動掛載的管理
傳統的 linux 系統中,使用者可以用 /etc/fstab 檔案來維護固定的檔案系統掛載點。這些掛載點在系統啟動過程中被自動掛載,一旦啟動過程結束,這些掛載點就會確儲存在。這些掛載點都是對系統執行至關重要的檔案系統,比如 HOME 目錄。和 sysvinit 一樣,Systemd 管理這些掛載點,以便能夠在系統啟動時自動掛載它們。systemd 還相容 /etc/fstab 檔案,您可以繼續使用該檔案管理掛載點。
有時候使用者還需要動態掛載點,比如打算存取 DVD 或者 NFS 共用的內容時,才臨時執行掛載以便存取其中的內容,而不存取光碟時該掛載點被取消(umount),以便節約資源。傳統地,人們依賴 autofs 服務來實現這種功能。
systemd 內建了自動掛載服務,無需另外安裝 autofs 服務,可以直接使用 systemd 提供的自動掛載管理能力來實現 autofs 的功能。
實現事務性依賴關係管理
系統啟動過程是由很多的獨立工作共同組成的,這些工作之間可能存在依賴關係,比如掛載一個 NFS 檔案系統必須依賴網路能夠正常工作。systemd 雖然能夠最大限度地並行執行很多有依賴關係的工作,但是類似"掛載 NFS"和"啟動網路"這樣的工作還是存在天生的先後依賴關係,無法並行執行。對於這些任務,systemd 維護一個"事務一致性"的概念,保證所有相關的服務都可以正常啟動而不會出現互相依賴,以至於死鎖的情況。
紀錄檔服務
systemd 自帶紀錄檔服務 journald,該紀錄檔服務的設計初衷是克服現有的 syslog 服務的缺點。比如:
- syslog 不安全,訊息的內容無法驗證。每一個本地進程都可以聲稱自己是 Apache PID 4711,而 syslog 也就相信並儲存到磁碟上。
- 資料沒有嚴格的格式,非常隨意。自動化的紀錄檔分析器需要分析人類語言字串來識別訊息。一方面此類分析困難低效;此外紀錄檔格式的變化會導致分析程式碼需要更新甚至重寫。
systemd journal 用二進位制格式儲存所有紀錄檔資訊,使用者使用 journalctl 命令來檢視紀錄檔資訊。無需自己編寫複雜脆弱的字串分析處理程式。
systemd journal 的優點如下:
簡單性:程式碼少,依賴少,抽象開銷最小。
零維護:紀錄檔是除錯和監控系統的核心功能,因此它自己不能再產生問題。舉例說,自動管理磁碟空間,避免由於紀錄檔的不斷產生而將磁碟空間耗盡。
移植性:紀錄檔檔案應該在所有型別的 Linux 系統上可用,無論它使用的何種 CPU 或者位元組序。
效能:新增和瀏覽紀錄檔非常快。
最小資源佔用:紀錄檔資料檔案需要較小。
統一化:各種不同的紀錄檔儲存技術應該統一起來,將所有的可記錄事件儲存在同一個資料儲存中。所以紀錄檔內容的全域性上下文都會被儲存並且可供日後查詢。例如一條韌體記錄後通常會跟隨一條核心記錄,最終還會有一條使用者態記錄。重要的是當儲存到硬碟上時這三者之間的關係不會丟失。syslog 將不同的資訊儲存到不同的檔案中,分析的時候很難確定哪些條目是相關的。
擴充套件性:紀錄檔的適用範圍很廣,從嵌入式裝置到超級計算機叢集都可以滿足需求。
安全性:紀錄檔檔案是可以驗證的,讓無法檢測的修改不再可能。
在了解了 systemd 的種種優勢之後讓我們開始認識它的一些基本概念。
unit(單元)
系統初始化需要做的事情非常多。需要啟動後台服務,比如啟動 ssh 服務;需要做設定工作,比如掛載檔案系統。這個過程中的每一步都被 systemd 抽象為一個設定單元,即 unit。可以認為一個服務是一個設定單元,一個掛載點是一個設定單元,一個交換分割區的設定是一個設定單元等等。systemd 將設定單元歸納為以下一些不同的型別。然而,systemd 正在快速發展,新功能不斷增加。所以設定單元型別可能在不久的將來繼續增加。下面是一些常見的 unit 型別:
service :代表一個後台服務進程,比如 MySQLd。這是最常用的一類。
socket :此類設定單元封裝系統和網際網路中的一個通訊端 。當下,systemd 支援流式、資料包和連續包的 AF_INET、AF_INET6、AF_UNIX socket 。每一個通訊端設定單元都有一個相應的服務設定單元 。相應的服務在第一個"連線"進入通訊端時就會啟動(例如:nscd.socket 在有新連線後便啟動 nscd.service)。
device :此類設定單元封裝一個存在於 Linux 裝置樹中的裝置。每一個使用 udev 規則標記的裝置都將會在 systemd 中作為一個裝置設定單元出現。
mount :此類設定單元封裝檔案系統結構層次中的一個掛載點。Systemd 將對這個掛載點進行監控和管理。比如可以在啟動時自動將其掛載;可以在某些條件下自動解除安裝。Systemd 會將 /etc/fstab 中的條目都轉換為掛載點,並在開機時處理。
automount :此類設定單元封裝系統結構層次中的一個自掛載點。每一個自掛載設定單元對應一個掛載設定單元 ,當該自動掛載點被存取時,systemd 執行掛載點中定義的掛載行為。
swap:和掛載設定單元類似,交換設定單元用來管理交換分割區。使用者可以用交換設定單元來定義系統中的交換分割區,可以讓這些交換分割區在啟動時被啟用。
target :此類設定單元為其他設定單元進行邏輯分組。它們本身實際上並不做什麼,只是參照其他設定單元而已。這樣便可以對設定單元做一個統一的控制。這樣就可以實現大家都已經非常熟悉的執行級別概念。比如想讓系統進入圖形化模式,需要執行許多服務和設定命令,這些操作都由一個個的設定單元表示,將所有這些設定單元組合為一個目標(target),就表示需要將這些設定單元全部執行一遍以便進入目標所代表的系統執行狀態。 (例如:multi-user.target 相當於在傳統使用 SysV 的系統中執行級別 5)
timer:定時器設定單元用來定時觸發使用者定義的操作,這類設定單元取代了 atd、crond 等傳統的定時服務。
snapshot :與 target 設定單元相似,快照是一組設定單元。它儲存了系統當前的執行狀態。
path:檔案系統中的一個檔案或目錄。
scope:用於 cgroups,表示從 systemd 外部建立的進程。
slice:用於 cgroups,表示一組按層級排列的單位。slice 並不包含進程,但會組建一個層級,並將 scope 和 service 都放置其中。
每個設定單元都有一個對應的組態檔,系統管理員的任務就是編寫和維護這些不同的組態檔,比如一個 MySQL 服務對應一個 mysql.service 檔案。這種組態檔的語法非常簡單,使用者不需要再編寫和維護複雜的系統指令碼了。
依賴關係
雖然 systemd 將大量的啟動工作解除了依賴,使得它們可以並行啟動。但還是存在有些任務,它們之間存在天生的依賴,不能用"通訊端啟用"(socket activation)、D-Bus activation 和 autofs 三大方法來解除依賴。比如:掛載必須等待掛載點在檔案系統中被建立;掛載也必須等待相應的物理裝置就緒。為了解決這類依賴問題,systemd 的設定單元之間可以彼此定義依賴關係。Systemd 用設定單元定義檔案中的關鍵字來描述設定單元之間的依賴關係。比如:unit A 依賴 unit B,可以在 unit B 的定義中用"require A"來表示。這樣 systemd 就會保證先啟動 A 再啟動 B。
systemd 事務
systemd 能保證事務完整性。Systemd 的事務概念和資料庫中的有所不同,主要是為了保證多個依賴的設定單元之間沒有環形參照。比如存在 unit A、B、C,假如它們的依賴關係如下(此圖來自網際網路):
存在迴圈依賴,那麼 systemd 將無法啟動任意一個服務。此時 systemd 將會嘗試解決這個問題,因為設定單元之間的依賴關係有兩種:required 是強依賴;want 則是弱依賴,systemd 將去掉 wants 關鍵字指定的依賴看看是否能打破迴圈。如果無法修復,systemd 會報錯。systemd 能夠自動檢測和修復這類設定錯誤,從而極大地減輕了管理員的排錯負擔。
target 和執行級別
systemd 用目標(target)替代了執行級別的概念,提供了更大的靈活性,如您可以繼承一個已有的目標,並新增其它服務,來建立自己的目標。下表列舉了 systemd 中的 target 和 sysvinit 中常見的 runlevel 的對應關係:
sysvinit runlevel | systemd target | 描述 |
0 | poweroff.target | 關閉系統。 |
1,s,single | rescue.target | 單使用者模式。 |
2,4 | multi-user.target | 使用者定義/域特定執行級別。預設等同於 3。 |
3 | multi-user.target | 多使用者,非圖形化。使用者可以通過多個控制台或網路登入。 |
5 | graphical.target | 多使用者,圖形化。通常為所有執行級別 3 的服務外加圖形化登入。 |
6 | reboot.target | 重新啟動。 |
emergency | emergency.target | 緊急 Shell。 |
總結
本文簡要的介紹了 init 系統的發展歷史,並概要的介紹了 systemd 的基本概念。由於相比其它的 init 系統優勢巨大,所以 systemd 已經被各大 linux 版本接受,並有望在 linux init 系統中一統天下。
CentOS7進程管理systemd詳解 http://www.linuxidc.com/Linux/2016-09/135464.htm
CentOS7/RHEL7 systemd詳解 http://www.linuxidc.com/Linux/2015-04/115937.htm
為什麼systemd會被如此迅速的採用? http://www.linuxidc.com/Linux/2014-08/105789.htm
systemd 與 sysVinit 彩版對照表 http://www.linuxidc.com/Linux/2014-09/106455.htm
太有用了!用systemd命令來管理Linux系統! http://www.linuxidc.com/Linux/2014-09/106490.htm
淺析 Linux 初始化 init 系統,第 3 部分: Systemd http://www.linuxidc.com/Linux/2014-12/110383.htm
本文永久更新連結地址:https://www.linuxidc.com/Linux/2018-03/151291.htm
相關文章