天天看點

淺析init和systemd

近年來,Linux 系統的 init 程序經曆了兩次重大的演進,傳統的 sysvinit 已經逐漸淡出曆史舞台,新的 UpStart 和 systemd 各有特點,越來越多的 Linux 發行版采納了 systemd。本文簡要介紹了這三種 init 系統的使用和原理,每個 Linux 系統管理者和系統軟體開發者都應該了解它們,以便更好地管理系統和開發應用。本文是系列的第 3 部分,主要講述 systemd 的特點和使用。

Systemd 的簡介和特點

Systemd 是 Linux 系統中最新的初始化系統(init),它主要的設計目标是克服 sysvinit 固有的缺點,提高系統的啟動速度。systemd 和 ubuntu 的 upstart 是競争對手,預計會取代 UpStart,實際上在作者寫作本文時,已經有消息稱 Ubuntu 也将采用 systemd 作為其标準的系統初始化系統。

Systemd 的很多概念來源于蘋果 Mac OS 作業系統上的 launchd,不過 launchd 專用于蘋果系統,是以長期未能獲得應有的廣泛關注。Systemd 借鑒了很多 launchd 的思想,它的重要特性如下:

同 SysVinit 和 LSB init scripts 相容

Systemd 是一個"新來的",Linux 上的很多應用程式并沒有來得及為它做相應的改變。和 UpStart 一樣,systemd 引入了新的配置方式,對應用程式的開發也有一些新的要求。如果 systemd 想替代目前正在運作的初始化系統,就必須和現有程式相容。任何一個 Linux 發行版都很難為了采用 systemd 而在短時間内将所有的服務代碼都修改一遍。

Systemd 提供了和 Sysvinit 以及 LSB initscripts 相容的特性。系統中已經存在的服務和程序無需修改。這降低了系統向 systemd 遷移的成本,使得 systemd 替換現有初始化系統成為可能。

更快的啟動速度

Systemd 提供了比 UpStart 更激進的并行啟動能力,采用了 socket / D-Bus activation 等技術啟動服務。一個顯而易見的結果就是:更快的啟動速度。

為了減少系統啟動時間,systemd 的目标是:

  • 盡可能啟動更少的程序
  • 盡可能将更多程序并行啟動

同樣地,UpStart 也試圖實作這兩個目标。UpStart 采用事件驅動機制,服務可以暫不啟動,當需要的時候才通過事件觸發其啟動,這符合第一個設計目标;此外,不相幹的服務可以并行啟動,這也實作了第二個目标。

下面的圖形示範了 UpStart 相對于 SysVInit 在并發啟動這個方面的改進:

圖 1. UpStart 對 SysVinit 的改進
淺析init和systemd

假設有 7 個不同的啟動項目, 比如 JobA、Job B 等等。在 SysVInit 中,每一個啟動項目都由一個獨立的腳本負責,它們由 sysVinit 順序地,串行地調用。是以總的啟動時間為 T1+T2+T3+T4+T5+T6+T7。其中一些任務有依賴關系,比如 A,B,C,D。

而 Job E 和 F 卻和 A,B,C,D 無關。這種情況下,UpStart 能夠并發地運作任務{E,F,(A,B,C,D)},使得總的啟動時間減少為 T1+T2+T3。

這無疑增加了系統啟動的并行性,進而提高了系統啟動速度。但是在 UpStart 中,有依賴關系的服務還是必須先後啟動。比如任務 A,B,(C,D)因為存在依賴關系,是以在這個局部,還是串行執行。

讓我們例舉一些例子, Avahi 服務需要 D-Bus 提供的功能,是以 Avahi 的啟動依賴于 D-Bus,UpStart 中,Avahi 必須等到 D-Bus 啟動就緒之後才開始啟動。類似的,livirtd 和 X11 都需要 HAL 服務先啟動,而所有這些服務都需要 syslog 服務記錄日志,是以它們都必須等待 syslog 服務先啟動起來。然而 httpd 和他們都沒有關系,是以 httpd 可以和 Avahi 等服務并發啟動。

Systemd 能夠更進一步提高并發性,即便對于那些 UpStart 認為存在互相依賴而必須串行的服務,比如 Avahi 和 D-Bus 也可以并發啟動。進而實作如下圖所示的并發啟動過程:

圖 2. systemd 的并發啟動
淺析init和systemd

所有的任務都同時并發執行,總的啟動時間被進一步降低為 T1。

可見 systemd 比 UpStart 更進一步提高了并行啟動能力,極大地加速了系統啟動時間。

systemd 提供按需啟動能力

當 sysvinit 系統初始化的時候,它會将所有可能用到的背景服務程序全部啟動運作。并且系統必須等待所有的服務都啟動就緒之後,才允許使用者登入。這種做法有兩個缺點:首先是啟動時間過長;其次是系統資源浪費。

某些服務很可能在很長一段時間内,甚至整個伺服器運作期間都沒有被使用過。比如 CUPS,列印服務在多數伺服器上很少被真正使用到。您可能沒有想到,在很多伺服器上 SSHD 也是很少被真正通路到的。花費在啟動這些服務上的時間是不必要的;同樣,花費在這些服務上的系統資源也是一種浪費。

Systemd 可以提供按需啟動的能力,隻有在某個服務被真正請求的時候才啟動它。當該服務結束,systemd 可以關閉它,等待下次需要時再次啟動它。

Systemd 采用 Linux 的 Cgroup 特性跟蹤和管理程序的生命周期

init 系統的一個重要職責就是負責跟蹤和管理服務程序的生命周期。它不僅可以啟動一個服務,也必須也能夠停止服務。這看上去沒有什麼特别的,然而在真正用代碼實作的時候,您或許會發現停止服務比一開始想的要困難。

服務程序一般都會作為精靈程序(daemon)在背景運作,為此服務程式有時候會派生(fork)兩次。在 UpStart 中,需要在配置檔案中正确地配置 expect 小節。這樣 UpStart 通過對 fork 系統調用進行計數,進而獲知真正的精靈程序的 PID 号。比如圖 3 所示的例子:

圖 3. 找到正确 pid
淺析init和systemd

如果 UpStart 找錯了,将 p1`作為服務程序的 Pid,那麼停止服務的時候,UpStart 會試圖殺死 p1`程序,而真正的 p1``程序則繼續執行。換句話說該服務就失去控制了。

還有更加特殊的情況。比如,一個 CGI 程式會派生兩次,進而脫離了和 Apache 的父子關系。當 Apache 程序被停止後,該 CGI 程式還在繼續運作。而我們希望服務停止後,所有由它所啟動的相關程序也被停止。

為了處理這類問題,UpStart 通過 strace 來跟蹤 fork、exit 等系統調用,但是這種方法很笨拙,且缺乏可擴充性。systemd 則利用了 Linux 核心的特性即 CGroup 來完成跟蹤的任務。當停止服務時,通過查詢 CGroup,systemd 可以確定找到所有的相關程序,進而幹淨地停止服務。

CGroup 已經出現了很久,它主要用來實作系統資源配額管理。CGroup 提供了類似檔案系統的接口,使用友善。當程序建立子程序時,子程序會繼承父程序的 CGroup。是以無論服務如何啟動新的子程序,所有的這些相關程序都會屬于同一個 CGroup,systemd 隻需要簡單地周遊指定的 CGroup 即可正确地找到所有的相關程序,将它們一一停止即可。

啟動挂載點和自動挂載的管理

傳統的 Linux 系統中,使用者可以用/etc/fstab 檔案來維護固定的檔案系統挂載點。這些挂載點在系統啟動過程中被自動挂載,一旦啟動過程結束,這些挂載點就會确儲存在。這些挂載點都是對系統運作至關重要的檔案系統,比如 HOME 目錄。和 sysvinit 一樣,Systemd 管理這些挂載點,以便能夠在系統啟動時自動挂載它們。Systemd 還相容/etc/fstab 檔案,您可以繼續使用該檔案管理挂載點。

有時候使用者還需要動态挂載點,比如打算通路 DVD 内容時,才臨時執行挂載以便通路其中的内容,而不通路CD光牒時該挂載點被取消(umount),以便節約資源。傳統地,人們依賴 autofs 服務來實作這種功能。

Systemd 内建了自動挂載服務,無需另外安裝 autofs 服務,可以直接使用 systemd 提供的自動挂載管理能力來實作 autofs 的功能。

實作事務性依賴關系管理

系統啟動過程是由很多的獨立工作共同組成的,這些工作之間可能存在依賴關系,比如挂載一個 NFS 檔案系統必須依賴網絡能夠正常工作。Systemd 雖然能夠最大限度地并發執行很多有依賴關系的工作,但是類似"挂載 NFS"和"啟動網絡"這樣的工作還是存在天生的先後依賴關系,無法并發執行。對于這些任務,systemd 維護一個"事務一緻性"的概念,保證所有相關的服務都可以正常啟動而不會出現互相依賴,以至于死鎖的情況。

能夠對系統進行快照和恢複

systemd 支援按需啟動,是以系統的運作狀态是動态變化的,人們無法準确地知道系統目前運作了哪些服務。Systemd 快照提供了一種将目前系統運作狀态儲存并恢複的能力。

比如系統目前正運作服務 A 和 B,可以用 systemd 指令行對目前系統運作狀況建立快照。然後将程序 A 停止,或者做其他的任意的對系統的改變,比如啟動新的程序 C。在這些改變之後,運作 systemd 的快照恢複指令,就可立即将系統恢複到快照時刻的狀态,即隻有服務 A,B 在運作。一個可能的應用場景是調試:比如伺服器出現一些異常,為了調試使用者将目前狀态儲存為快照,然後可以進行任意的操作,比如停止服務等等。等調試結束,恢複快照即可。

這個快照功能目前在 systemd 中并不完善,似乎開發人員也沒有特别關注它,是以有報告指出它還存在一些使用上的問題,使用時尚需慎重。

日志服務

systemd 自帶日志服務 journald,該日志服務的設計初衷是克服現有的 syslog 服務的缺點。比如:

  • syslog 不安全,消息的内容無法驗證。每一個本地程序都可以聲稱自己是 Apache PID 4711,而 syslog 也就相信并儲存到磁盤上。
  • 資料沒有嚴格的格式,非常随意。自動化的日志分析器需要分析人類語言字元串來識别消息。一方面此類分析困難低效;此外日志格式的變化會導緻分析代碼需要更新甚至重寫。

Systemd Journal 用二進制格式儲存所有日志資訊,使用者使用 journalctl 指令來檢視日志資訊。無需自己編寫複雜脆弱的字元串分析處理程式。

Systemd Journal 的優點如下:

  • 簡單性:代碼少,依賴少,抽象開銷最小。
  • 零維護:日志是除錯和監控系統的核心功能,是以它自己不能再産生問題。舉例說,自動管理磁盤空間,避免由于日志的不斷産生而将磁盤空間耗盡。
  • 移植性:日志 檔案應該在所有類型的 Linux 系統上可用,無論它使用的何種 CPU 或者位元組序。
  • 性能:添加和浏覽 日志 非常快。
  • 最小資源占用:日志 資料檔案需要較小。
  • 統一化:各種不同的日志存儲技術應該統一起來,将所有的可記錄事件儲存在同一個資料存儲中。是以日志内容的全局上下文都會被儲存并且可供日後查詢。例如一條固件記錄後通常會跟随一條核心記錄,最終還會有一條使用者态記錄。重要的是當儲存到硬碟上時這三者之間的關系不會丢失。Syslog 将不同的資訊儲存到不同的檔案中,分析的時候很難确定哪些條目是相關的。
  • 擴充性:日志的适用範圍很廣,從嵌入式裝置到超級計算機叢集都可以滿足需求。
  • 安全性:日志 檔案是可以驗證的,讓無法檢測的修改不再可能。

回頁首

Systemd 的基本概念

單元的概念

系統初始化需要做的事情非常多。需要啟動背景服務,比如啟動 SSHD 服務;需要做配置工作,比如挂載檔案系統。這個過程中的每一步都被 systemd 抽象為一個配置單元,即 unit。可以認為一個服務是一個配置單元;一個挂載點是一個配置單元;一個交換分區的配置是一個配置單元;等等。systemd 将配置單元歸納為以下一些不同的類型。然而,systemd 正在快速發展,新功能不斷增加。是以配置單元類型可能在不久的将來繼續增加。

  • 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 配置單元相似,快照是一組配置單元。它儲存了系統目前的運作狀态。

每個配置單元都有一個對應的配置檔案,系統管理者的任務就是編寫和維護這些不同的配置檔案,比如一個 MySQL 服務對應一個 mysql.service 檔案。這種配置檔案的文法非常簡單,使用者不需要再編寫和維護複雜的系統 5 腳本了。

依賴關系

雖然 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,假如它們的依賴關系為:

圖 4, Unit 的循環依賴
淺析init和systemd

存在循環依賴,那麼 systemd 将無法啟動任意一個服務。此時 systemd 将會嘗試解決這個問題,因為配置單元之間的依賴關系有兩種:required 是強依賴;want 則是弱依賴,systemd 将去掉 wants 關鍵字指定的依賴看看是否能打破循環。如果無法修複,systemd 會報錯。

Systemd 能夠自動檢測和修複這類配置錯誤,極大地減輕了管理者的排錯負擔。

Target 和運作級别

systemd 用目标(target)替代了運作級别的概念,提供了更大的靈活性,如您可以繼承一個已有的目标,并添加其它服務,來建立自己的目标。下表列舉了 systemd 下的目标和常見 runlevel 的對應關系:

表 1. Sysvinit 運作級别和 systemd 目标的對應表
Sysvinit 運作級别 Systemd 目标 備注
runlevel0.target, poweroff.target 關閉系統。
1, s, single runlevel1.target, rescue.target 單使用者模式。
2, 4 runlevel2.target, runlevel4.target, multi-user.target 使用者定義/域特定運作級别。預設等同于 3。
3 runlevel3.target, multi-user.target 多使用者,非圖形化。使用者可以通過多個控制台或網絡登入。
5 runlevel5.target, graphical.target 多使用者,圖形化。通常為所有運作級别 3 的服務外加圖形化登入。
6 runlevel6.target, reboot.target 重新開機
emergency emergency.target 緊急 Shell

回頁首

Systemd 的并發啟動原理

如前所述,在 Systemd 中,所有的服務都并發啟動,比如 Avahi、D-Bus、livirtd、X11、HAL 可以同時啟動。乍一看,這似乎有點兒問題,比如 Avahi 需要 syslog 的服務,Avahi 和 syslog 同時啟動,假設 Avahi 的啟動比較快,是以 syslog 還沒有準備好,可是 Avahi 又需要記錄日志,這豈不是會出現問題?

Systemd 的開發人員仔細研究了服務之間互相依賴的本質問題,發現所謂依賴可以分為三個具體的類型,而每一個類型實際上都可以通過相應的技術解除依賴關系。

并發啟動原理之一:解決 socket 依賴

絕大多數的服務依賴是套接字依賴。比如服務 A 通過一個套接字端口 S1 提供自己的服務,其他的服務如果需要服務 A,則需要連接配接 S1。是以如果服務 A 尚未啟動,S1 就不存在,其他的服務就會得到啟動錯誤。是以傳統地,人們需要先啟動服務 A,等待它進入就緒狀态,再啟動其他需要它的服務。Systemd 認為,隻要我們預先把 S1 建立好,那麼其他所有的服務就可以同時啟動而無需等待服務 A 來建立 S1 了。如果服務 A 尚未啟動,那麼其他程序向 S1 發送的服務請求實際上會被 Linux 作業系統緩存,其他程序會在這個請求的地方等待。一旦服務 A 啟動就緒,就可以立即處理緩存的請求,一切都開始正常運作。

那麼服務如何使用由 init 程序建立的套接字呢?

Linux 作業系統有一個特性,當程序調用 fork 或者 exec 建立子程序之後,所有在父程序中被打開的檔案句柄 (file descriptor) 都被子程序所繼承。套接字也是一種檔案句柄,程序 A 可以建立一個套接字,此後當程序 A 調用 exec 啟動一個新的子程序時,隻要確定該套接字的 close_on_exec 标志位被清空,那麼新的子程序就可以繼承這個套接字。子程序看到的套接字和父程序建立的套接字是同一個系統套接字,就仿佛這個套接字是子程序自己建立的一樣,沒有任何差別。

這個特性以前被一個叫做 inetd 的系統服務所利用。Inetd 程序會負責監控一些常用套接字端口,比如 Telnet,當該端口有連接配接請求時,inetd 才啟動 telnetd 程序,并把有連接配接的套接字傳遞給新的 telnetd 程序進行處理。這樣,當系統沒有 telnet 用戶端連接配接時,就不需要啟動 telnetd 程序。Inetd 可以代理很多的網絡服務,這樣就可以節約很多的系統負載和記憶體資源,隻有當有真正的連接配接請求時才啟動相應服務,并把套接字傳遞給相應的服務程序。

和 inetd 類似,systemd 是所有其他程序的父程序,它可以先建立所有需要的套接字,然後在調用 exec 的時候将該套接字傳遞給新的服務程序,而新程序直接使用該套接字進行服務即可。

并發啟動原理之二:解決 D-Bus 依賴

D-Bus 是 desktop-bus 的簡稱,是一個低延遲、低開銷、高可用性的程序間通信機制。它越來越多地用于應用程式之間通信,也用于應用程式和作業系統核心之間的通信。很多現代的服務程序都使用D-Bus 取代套接字作為程序間通信機制,對外提供服務。比如簡化 Linux 網絡配置的 NetworkManager 服務就使用 D-Bus 和其他的應用程式或者服務進行互動:郵件用戶端軟體 evolution 可以通過 D-Bus 從 NetworkManager 服務擷取網絡狀态的改變,以便做出相應的處理。

D-Bus 支援所謂"bus activation"功能。如果服務 A 需要使用服務 B 的 D-Bus 服務,而服務 B 并沒有運作,則 D-Bus 可以在服務 A 請求服務 B 的 D-Bus 時自動啟動服務 B。而服務 A 發出的請求會被 D-Bus 緩存,服務 A 會等待服務 B 啟動就緒。利用這個特性,依賴 D-Bus 的服務就可以實作并行啟動。

并發啟動原理之三:解決檔案系統依賴

系統啟動過程中,檔案系統相關的活動是最耗時的,比如挂載檔案系統,對檔案系統進行磁盤檢查(fsck),磁盤配額檢查等都是非常耗時的操作。在等待這些工作完成的同時,系統處于空閑狀态。那些想使用檔案系統的服務似乎必須等待檔案系統初始化完成才可以啟動。但是 systemd 發現這種依賴也是可以避免的。

Systemd 參考了 autofs 的設計思路,使得依賴檔案系統的服務和檔案系統本身初始化兩者可以并發工作。autofs 可以監測到某個檔案系統挂載點真正被通路到的時候才觸發挂載操作,這是通過核心 automounter 子產品的支援而實作的。比如一個 open()系統調用作用在"/misc/cd/file1"的時候,/misc/cd 尚未執行挂載操作,此時 open()調用被挂起等待,Linux 核心通知 autofs,autofs 執行挂載。這時候,控制權傳回給 open()系統調用,并正常打開檔案。

Systemd 內建了 autofs 的實作,對于系統中的挂載點,比如/home,當系統啟動的時候,systemd 為其建立一個臨時的自動挂載點。在這個時刻/home 真正的挂載裝置尚未啟動好,真正的挂載操作還沒有執行,檔案系統檢測也還沒有完成。可是那些依賴該目錄的程序已經可以并發啟動,他們的 open()操作被内建在 systemd 中的 autofs 捕獲,将該 open()調用挂起(可中斷睡眠狀态)。然後等待真正的挂載操作完成,檔案系統檢測也完成後,systemd 将該自動挂載點替換為真正的挂載點,并讓 open()調用傳回。由此,實作了那些依賴于檔案系統的服務和檔案系統本身同時并發啟動。

當然對于"/"根目錄的依賴實際上一定還是要串行執行,因為 systemd 自己也存放在/之下,必須等待系統根目錄挂載檢查好。

不過對于類似/home 等挂載點,這種并發可以提高系統的啟動速度,尤其是當/home 是遠端的 NFS 節點,或者是加密盤等,需要耗費較長的時間才可以準備就緒的情況下,因為并發啟動,這段時間内,系統并不是完全無事可做,而是可以利用這段空餘時間做更多的啟動程序的事情,總的來說就縮短了系統啟動時間。

回頁首

Systemd 的使用

下面針對技術人員的不同角色來簡單地介紹一下 systemd 的使用。本文隻打算給出簡單的描述,讓您對 systemd 的使用有一個大概的了解。具體的細節内容太多,即無法在一篇短文内寫全,本人也沒有那麼強大的能力。還需要讀者自己去進一步查閱 systemd 的文檔。

系統軟體開發人員

開發人員需要了解 systemd 的更多細節。比如您打算開發一個新的系統服務,就必須了解如何讓這個服務能夠被 systemd 管理。這需要您注意以下這些要點:

  • 背景服務程序代碼不需要執行兩次派生來實作背景精靈程序,隻需要實作服務本身的主循環即可。
  • 不要調用 setsid(),交給 systemd 處理
  • 不再需要維護 pid 檔案。
  • Systemd 提供了日志功能,服務程序隻需要輸出到 stderr 即可,無需使用 syslog。
  • 處理信号 SIGTERM,這個信号的唯一正确作用就是停止目前服務,不要做其他的事情。
  • SIGHUP 信号的作用是重新開機服務。
  • 需要套接字的服務,不要自己建立套接字,讓 systemd 傳入套接字。
  • 使用 sd_notify()函數通知 systemd 服務自己的狀态改變。一般地,當服務初始化結束,進入服務就緒狀态時,可以調用它。

Unit 檔案的編寫

對于開發者來說,工作量最大的部分應該是編寫配置單元檔案,定義所需要的單元。

舉例來說,開發人員開發了一個新的服務程式,比如 httpd,就需要為其編寫一個配置單元檔案以便該服務可以被 systemd 管理,類似 UpStart 的工作配置檔案。在該檔案中定義服務啟動的指令行文法,以及和其他服務的依賴關系等。

此外我們之前已經了解到,systemd 的功能繁多,不僅用來管理服務,還可以管理挂載點,定義定時任務等。這些工作都是由編輯相應的配置單元檔案完成的。我在這裡給出幾個配置單元檔案的例子。

下面是 SSH 服務的配置單元檔案,服務配置單元檔案以.service 為檔案名字尾。

  #cat /etc/system/system/sshd.service
  [Unit]
  Description=OpenSSH server daemon
  [Service]
  EnvironmentFile=/etc/sysconfig/sshd
  ExecStartPre=/usr/sbin/sshd-keygen
  ExecStart=/usrsbin/sshd –D $OPTIONS
  ExecReload=/bin/kill –HUP $MAINPID
  KillMode=process
  Restart=on-failure
  RestartSec=42s
  [Install]
  WantedBy=multi-user.target      

檔案分為三個小節。第一個是[Unit]部分,這裡僅僅有一個描述資訊。第二部分是 Service 定義,其中,ExecStartPre 定義啟動服務之前應該運作的指令;ExecStart 定義啟動服務的具體指令行文法。第三部分是[Install],WangtedBy 表明這個服務是在多使用者模式下所需要的。

那我們就來看下 multi-user.target 吧:

  #cat multi-user.target
  [Unit]
  Description=Multi-User System
  Documentation=man.systemd.special(7)
  Requires=basic.target
  Conflicts=rescue.service rescure.target
  After=basic.target rescue.service rescue.target
  AllowIsolate=yes
  [Install]
  Alias=default.target      

第一部分中的 Requires 定義表明 multi-user.target 啟動的時候 basic.target 也必須被啟動;另外 basic.target 停止的時候,multi-user.target 也必須停止。如果您接着檢視 basic.target 檔案,會發現它又指定了 sysinit.target 等其他的單元必須随之啟動。同樣 sysinit.target 也會包含其他的單元。采用這樣的層層連結的結構,最終所有需要支援多使用者模式的元件服務都會被初始化啟動好。

在[Install]小節中有 Alias 定義,即定義本單元的别名,這樣在運作 systemctl 的時候就可以使用這個别名來引用本單元。這裡的别名是 default.target,比 multi-user.target 要簡單一些。。。

此外在/etc/systemd/system 目錄下還可以看到諸如*.wants 的目錄,放在該目錄下的配置單元檔案等同于在[Unit]小節中的 wants 關鍵字,即本單元啟動時,還需要啟動這些單元。比如您可以簡單地把您自己寫的 foo.service 檔案放入 multi-user.target.wants 目錄下,這樣每次都會被預設啟動了。

最後,讓我們來看看 sys-kernel-debug.mout 檔案,這個檔案定義了一個檔案挂載點:

#cat sys-kernel-debug.mount
[Unit]
Description=Debug File Syste
DefaultDependencies=no
ConditionPathExists=/sys/kernel/debug
Before=sysinit.target
[Mount]
What=debugfs
Where=/sys/kernel/debug
Type=debugfs      

這個配置單元檔案定義了一個挂載點。挂載配置單元檔案有一個[Mount]配置小節,裡面配置了 What,Where 和 Type 三個資料項。這都是挂載指令所必須的,例子中的配置等同于下面這個挂載指令:

mount –t debugfs /sys/kernel/debug debugfs

配置單元檔案的編寫需要很多的學習,必須參考 systemd 附帶的 man 等文檔進行深入學習。希望通過上面幾個小例子,大家已經了解配置單元檔案的作用和一般寫法了。

系統管理者

systemd 的主要指令行工具是 systemctl。

多數管理者應該都已經非常熟悉系統服務和 init 系統的管理,比如 service、chkconfig 以及 telinit 指令的使用。systemd 也完成同樣的管理任務,隻是指令工具 systemctl 的文法有所不同而已,是以用表格來對比 systemctl 和傳統的系統管理指令會非常清晰。

表 2. Systemd 指令和 sysvinit 指令的對照表
Sysvinit 指令 Systemd 指令 備注
service foo start systemctl start foo.service 用來啟動一個服務 (并不會重新開機現有的)
service foo stop systemctl stop foo.service 用來停止一個服務 (并不會重新開機現有的)。
service foo restart systemctl restart foo.service 用來停止并啟動一個服務。
service foo reload systemctl reload foo.service 當支援時,重新裝載配置檔案而不中斷等待操作。
service foo condrestart systemctl condrestart foo.service 如果服務正在運作那麼重新開機它。
service foo status systemctl status foo.service 彙報服務是否正在運作。
ls /etc/rc.d/init.d/ systemctl list-unit-files --type=service 用來列出可以啟動或停止的服務清單。
chkconfig foo on systemctl enable foo.service 在下次啟動時或滿足其他觸發條件時設定服務為啟用
chkconfig foo off systemctl disable foo.service 在下次啟動時或滿足其他觸發條件時設定服務為禁用
chkconfig foo systemctl is-enabled foo.service 用來檢查一個服務在目前環境下被配置為啟用還是禁用。
chkconfig –list systemctl list-unit-files --type=service 輸出在各個運作級别下服務的啟用和禁用情況
chkconfig foo –list ls /etc/systemd/system/*.wants/foo.service 用來列出該服務在哪些運作級别下啟用和禁用。
chkconfig foo –add systemctl daemon-reload 當您建立新服務檔案或者變更設定時使用。
telinit 3 systemctl isolate multi-user.target (OR systemctl isolate runlevel3.target OR telinit 3) 改變至多使用者運作級别。

除了表 2 列出的常見用法,系統管理者還需要了解其他一些系統配置和管理任務的改變。

首先我們了解 systemd 如何處理電源管理,指令如下表所示:

表 3,systemd 電源管理指令
指令 操作
systemctl reboot 重新開機機器
systemctl poweroff 關機
systemctl suspend 待機
systemctl hibernate 休眠
systemctl hybrid-sleep 混合休眠模式(同時休眠到硬碟并待機)

關機不是每個登入使用者在任何情況下都可以執行的,一般隻有管理者才可以關機。正常情況下系統不應該允許 SSH 遠端登入的使用者執行關機指令。否則其他使用者正在工作,一個使用者把系統關了就不好了。為了解決這個問題,傳統的 Linux 系統使用 ConsoleKit 跟蹤使用者登入情況,并決定是否賦予其關機的權限。現在 ConsoleKit 已經被 systemd 的 logind 所替代。

logind 不是 pid-1 的 init 程序。它的作用和 UpStart 的 session init 類似,但功能要豐富很多,它能夠管理幾乎所有使用者會話(session)相關的事情。logind 不僅是 ConsoleKit 的替代,它可以:

  • 維護,跟蹤會話和使用者登入情況。如上所述,為了決定關機指令是否可行,系統需要了解目前使用者登入情況,如果使用者從 SSH 登入,不允許其執行關機指令;如果普通使用者從本地登入,且該使用者是系統中的唯一會話,則允許其執行關機指令;這些判斷都需要 logind 維護所有的使用者會話和登入情況。
  • Logind 也負責統計使用者會話是否長時間沒有操作,可以執行休眠/關機等相應操作。
  • 為使用者會話的所有程序建立 CGroup。這不僅友善統計所有使用者會話的相關程序,也可以實作會話級别的系統資源控制。
  • 負責電源管理的組合鍵處理,比如使用者按下電源鍵,将系統切換至睡眠狀态。
  • 多席位(multi-seat) 管理。如今的電腦,即便一台筆記本電腦,也完全可以提供多人同時使用的計算能力。多席位就是一台電腦主機管理多個外設,比如兩個螢幕和兩個滑鼠/鍵盤。席位一使用螢幕 1 和鍵盤 1;席位二使用螢幕 2 和鍵盤 2,但他們都共享一台主機。使用者會話可以自由在多個席位之間切換。或者當插入新的鍵盤,螢幕等實體外設時,自動啟動 gdm 使用者登入界面等。所有這些都是多席位管理的内容。ConsoleKit 始終沒有實作這個功能,systemd 的 logind 能夠支援多席位。

以上描述的這些管理功能僅僅是 systemd 的部分功能,除此之外,systemd 還負責系統其他的管理配置,比如配置網絡,Locale 管理,管理系統核心子產品加載等,完整地描述它們已經超出了本人的能力。

回頁首

systemd 小結

在不才作者看來,作為系統初始化系統,systemd 的最大特點有兩個:

  • 令人驚奇的激進的并發啟動能力,極大地提高了系統啟動速度;
  • 用 CGroup 統計跟蹤子程序,幹淨可靠。

此外,和其前任不同的地方在于,systemd 已經不僅僅是一個初始化系統了。

Systemd 出色地替代了 sysvinit 的所有功能,但它并未就此自滿。因為 init 程序是系統所有程序的父程序這樣的特殊性,systemd 非常适合提供曾經由其他服務提供的功能,比如定時任務 (以前由 crond 完成) ;會話管理 (以前由 ConsoleKit/PolKit 等管理) 。僅僅從本文皮毛一樣的介紹來看,Systemd 已經管得很多了,可它還在不斷發展。它将逐漸成為一個多功能的系統環境,能夠處理非常多的系統管理任務,有人甚至将它看作一個作業系統。

好的一點是,這非常有助于标準化 Linux 的管理!從前,不同的 Linux 發行版各行其事,使用不同方法管理系統,從來也不會互相妥協。比如如何将系統進入休眠狀态,不同的系統有不同的解決方案,即便是同一個 Linux 系統,也存在不同的方法,比如一個有趣的讨論:如何讓 ubuntu 系統休眠,可以使用底層的/sys/power/state 接口,也可以使用諸如 pm-utility 等高層接口。存在這麼多種不同的方法做一件事情對像我這樣的普通使用者而言可不是件有趣的事情。systemd 提供統一的電源管理指令接口,這件事情的意義就類似全世界的人都說統一的語言,我們再也不需要學習外語了,多麼美好!

如果所有的 Linux 發行版都采納了 systemd,那麼系統管理任務便可以很大程度上實作标準化。此外 systemd 有個很棒的承諾:接口保持穩定,不會再輕易改動。對于軟體開發人員來說,這是多麼體貼又讓人感動的承諾啊!

回頁首

結束語

本系列文章從古老卻簡明穩定的 sysvinit 說起,接着簡要描述了 UpStart 帶來的清新改變,最後看到了充滿野心和活力的新生代 systemd 系統逐漸統治 Linux 的各個版本。就好像在看我們這個世界,一代人老去,新的一代帶着橫掃一切的氣概登上舞台,還沒有喊出他們最有力的口号,更猛的一代已經把聚光燈和所有的目光帶走。Systemd 之後也許還有更新的 init 系統出現吧,讓我們繼續期待。。。

http://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html

轉載于:https://blog.51cto.com/babylater/1895056

繼續閱讀