CentOS 7 巨大變更之 systemd 取代 SysV的Init

1 systemd是什麼

首先systmed是一個用戶空間的程序,屬於應用程序,不屬於Linux內核範疇,Linux內核的主要特徵在全部發行版中是統一的,廠商能夠自由改變的是用戶空間的應用程序。

Linux內核加載啓動後,用戶空間的第一個進程就是初始化進程,這個程序的物理文件約定位於/sbin/init,固然也能夠經過傳遞內核參數來讓內核啓動指定的程序。這個進程的特色是進程號爲1,表明第一個運行的用戶空間進程。不一樣發行版採用了不一樣的啓動程序,主要有如下幾種主流選擇:
(1)以Ubuntu爲表明的Linux發行版採用upstart。
(2)以7.0版本以前的CentOS爲表明的System V init。
(3)CentOS7.0版本的systemd。

下面是CentOS6.5和CentOS7兩個版本初始化進程的信息截圖。

CentOS6.5採用的是systemV init


CentOS7 採用的是systemd

2 Systemd物理文件組成

systemd是一個完整的軟件包,安裝完成後有不少物理文件組成,大體分佈爲,配置文件位於/etc/systemd這個目錄下,配置工具命令位於/bin,和/sbin這兩個目錄下,預先準備的備用配置文件位於/lib/systemd目錄下,還有庫文件和幫助手冊等等。這是一個龐大的軟件包。詳情使用rpm -ql systemd便可查看。

先讓咱們看看當前系統/etc/inittab這個文件的內容,這個文件是systme V init的標準配置文件,現在變成了:

[plain]  view plain copy
  1. # inittab is no longer used when using systemd.  
  2. #  
  3. # ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.  
  4. #  
  5. # Ctrl-Alt-Delete is handled by /etc/systemd/system/ctrl-alt-del.target  
  6. #  
  7. # systemd uses 'targets' instead of runlevels. By default, there are two main targets:  
  8. #  
  9. # multi-user.target: analogous to runlevel 3  
  10. # graphical.target: analogous to runlevel 5  
  11. #  
  12. # To set a default target, run:  
  13. #  
  14. # ln -sf /lib/systemd/system/<target name>.target /etc/systemd/system/default.target  

這說明,在systemd掌權後,inittab再也不起做用,也沒有了「運行級」的概念。如今起做用的配置文件是/etc/systemd/system/default.target這個文件了。此文件的內容以下:

[plain]  view plain copy
  1. #  This file is part of systemd.  
  2. #  
  3. #  systemd is free software; you can redistribute it and/or modify it  
  4. #  under the terms of the GNU Lesser General Public License as published by  
  5. #  the Free Software Foundation; either version 2.1 of the License, or  
  6. #  (at your option) any later version.  
  7.   
  8. [Unit]  
  9. Description=Multi-User System  
  10. Documentation=man:systemd.special(7)  
  11. Requires=basic.target  
  12. Conflicts=rescue.service rescue.target  
  13. After=basic.target rescue.service rescue.target  
  14. AllowIsolate=yes  
  15.   
  16. [Install]  
  17. Alias=default.target  

systemd的配置文件後綴根據配置單元類型的不一樣而不一樣,主要有.service,.target等。

3 Systemd運行原理

3.1 systemd的基本概念

(1)配置單元unit

系統初始化要作不少工做,如掛在文件系統,啓動sshd服務,配置交換分區,這均可以看作是一個配置單元,systemd安裝功能不一樣把配置單元分紅多種類型。
  • service 後代服務進程,如httpd,mysqld等
  • soket 對應一個套接字,以後對應到一個service,相似於xinetd的功能
  • device 對應udev規則標記的一個設備
  • mount 系統中的一個掛載點,systemd據此進行自動掛載,爲了與SystemV兼容,目前systemd自動處理/etc/fstab並轉化爲mount
  • automount 自動掛載點
  • swap 配置交換分區
  • target 配置單元的邏輯分組,包含多個相關的配置單元,能夠當成是SystemV中的運行級。
  • timer 定時器。用來定時觸發用戶定義的操做,它能夠用來取代傳統的atd,crond等。
  • snapshot 與target相似,表示當前的運行狀態
每個配置單元都有一個對應的配置文件,系統管理員的任務就是編寫和維護這寫不一樣的配置文件,好比一個MySql服務對應一個mysql.service文件。

(2)依賴關係

systemd並不能徹底解除各個單元之間的依賴關係,如物理設備單元準備就緒以前,不可能執行掛載單元。爲此須要定義各個單元之間的依賴關係。有依賴的地方就會有出現死循環的可能,好比A依賴於B,B依賴於C,C依賴於A,那麼致使死鎖。systemd爲此提供了兩種不一樣程度的依賴關係,一個是require,一個是want,出現死循環時,systemd會嘗試忽略want類型的依賴,如仍不能解鎖,那麼systemd報錯。

(3)Target和runlevel

前面說過,systemd使用target取代了systemV的運行級的概念。
表 1. Sysvinit 運行級別和 systemd 目標的對應表
Sysvinit 運行級別 Systemd 目標 備註
0 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 


3.2 systemd並行啓動原理

如前所述,在 Systemd 中,全部的服務都併發啓動,好比 Avahi、D-Bus、livirtd、X十一、HAL 能夠同時啓動。乍一看,這彷佛有點兒問題,好比 Avahi 須要 syslog 的服務,Avahi 和 syslog 同時啓動,假設 Avahi 的啓動比較快,因此 syslog 尚未準備好,但是 Avahi 又須要記錄日誌,這豈不是會出現問題? mysql

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

併發啓動原理之一:解決 socket 依賴

絕大多數的服務依賴是套接字依賴。好比服務 A 經過一個套接字端口 S1 提供本身的服務,其餘的服務若是須要服務 A,則須要鏈接 S1。所以若是服務 A 還沒有啓動,S1 就不存在,其餘的服務就會獲得啓動錯誤。因此傳統地,人們須要先啓動服務 A,等待它進入就緒狀態,再啓動其餘須要它的服務。Systemd 認爲,只要咱們預先把 S1 創建好,那麼其餘全部的服務就能夠同時啓動而無需等待服務 A 來建立 S1 了。若是服務 A 還沒有啓動,那麼其餘進程向 S1 發送的服務請求實際上會被 Linux 操做系統緩存,其餘進程會在這個請求的地方等待。一旦服務 A 啓動就緒,就能夠當即處理緩存的請求,一切都開始正常運行。 sql

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

Linux 操做系統有一個特性,當進程調用 fork 或者 exec 建立子進程以後,全部在父進程中被打開的文件句柄 (file descriptor) 都被子進程所繼承。套接字也是一種文件句柄,進程 A 能夠建立一個套接字,此後當進程 A 調用 exec 啓動一個新的子進程時,只要確保該套接字的 close_on_exec 標誌位被清空,那麼新的子進程就能夠繼承這個套接字。子進程看到的套接字和父進程建立的套接字是同一個系統套接字,就彷彿這個套接字是子進程本身建立的同樣,沒有任何區別。 緩存

這個特性之前被一個叫作 inetd 的系統服務所利用。Inetd 進程會負責監控一些經常使用套接字端口,好比 Telnet,當該端口有鏈接請求時,inetd 才啓動 telnetd 進程,並把有鏈接的套接字傳遞給新的 telnetd 進程進行處理。這樣,當系統沒有 telnet 客戶端鏈接時,就不須要啓動 telnetd 進程。Inetd 能夠代理不少的網絡服務,這樣就能夠節約不少的系統負載和內存資源,只有當有真正的鏈接請求時才啓動相應服務,並把套接字傳遞給相應的服務進程。 網絡

和 inetd 相似,systemd 是全部其餘進程的父進程,它能夠先創建全部須要的套接字,而後在調用 exec 的時候將該套接字傳遞給新的服務進程,而新進程直接使用該套接字進行服務便可。 session

併發啓動原理之二:解決 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 的服務就能夠實現並行啓動。 ssh

併發啓動原理之三:解決文件系統依賴

系統啓動過程當中,文件系統相關的活動是最耗時的,好比掛載文件系統,對文件系統進行磁盤檢查(fsck),磁盤配額檢查等都是很是耗時的操做。在等待這些工做完成的同時,系統處於空閒狀態。那些想使用文件系統的服務彷佛必須等待文件系統初始化完成才能夠啓動。可是 systemd 發現這種依賴也是能夠避免的。 socket

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 節點,或者是加密盤等,須要耗費較長的時間才能夠準備就緒的狀況下,由於併發啓動,這段時間內,系統並非徹底無事可作,而是能夠利用這段空餘時間作更多的啓動進程的事情,總的來講就縮短了系統啓動時間。



4 Systemd配置使用

4.1 對於系統開發人員

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

  • 後臺服務進程代碼不須要執行兩次派生來實現後臺精靈進程,只須要實現服務自己的主循環便可。
  • 不要調用 setsid(),交給 systemd 處理
  • 再也不須要維護 pid 文件。
  • Systemd 提供了日誌功能,服務進程只須要輸出到 stderr 便可,無需使用 syslog。
  • 處理信號 SIGTERM,這個信號的惟一正確做用就是中止當前服務,不要作其餘的事情。
  • SIGHUP 信號的做用是重啓服務。
  • 須要套接字的服務,不要本身建立套接字,讓 systemd 傳入套接字。
  • 使用 sd_notify()函數通知 systemd 服務本身的狀態改變。通常地,當服務初始化結束,進入服務就緒狀態時,能夠調用它。

對於開發者來講,工做量最大的部分應該是編寫配置單元文件,定義所須要的單元。

舉例來講,開發人員開發了一個新的服務程序,好比 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 等文檔進行深刻學習。但願經過上面幾個小例子,你們已經瞭解配置單元文件的做用和通常寫法了。

4.2 對於系統管理員

系統管理員的主要工具是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 管理,管理系統內核模塊加載等。

5 總結

在不才做者看來,做爲系統初始化系統,systemd 的最大特色有兩個:

  • 使人驚奇的激進的併發啓動能力,極大地提升了系統啓動速度;
  • 用 CGroup 統計跟蹤子進程,乾淨可靠。

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

Systemd 出色地替代了 sysvinit 的全部功能,但它並未就此自滿。由於 init 進程是系統全部進程的父進程這樣的特殊性,systemd 很是適合提供曾經由其餘服務提供的功能,好比定時任務 (之前由 crond 完成) ;會話管理 (之前由 ConsoleKit/PolKit 等管理) 。僅僅從本文皮毛同樣的介紹來看,Systemd 已經管得不少了,可它還在不斷髮展。它將逐漸成爲一個多功能的系統環境,可以處理很是多的系統管理任務,有人甚至將它看做一個操做系統。

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

若是全部的 Linux 發行版都採納了 systemd,那麼系統管理任務即可以很大程度上實現標準化。此外 systemd 有個很棒的承諾:接口保持穩定,不會再輕易改動。對於軟件開發人員來講,這是多麼體貼又讓人感動的承諾啊!

相關文章
相關標籤/搜索