標籤:
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 採用的是systemd2 Systemd物理檔案組成systemd是一個完整的軟體包,安裝完成後有很多物理檔案組成,大致分布為,設定檔位於/etc/systemd這個目錄下,組態工具命令位於/bin,和/sbin這兩個目錄下,預先準備的備用設定檔位於/lib/systemd目錄下,還有庫檔案和協助手冊等等。這是一個龐大的軟體包。詳情使用rpm -ql systemd即可查看。 先讓我們看看當前系統/etc/inittab這個檔案的內容,這個檔案是systme V init的標準設定檔,如今變成了:
# inittab is no longer used when using systemd.## ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.## Ctrl-Alt-Delete is handled by /etc/systemd/system/ctrl-alt-del.target## systemd uses ‘targets‘ instead of runlevels. By default, there are two main targets:## multi-user.target: analogous to runlevel 3# graphical.target: analogous to runlevel 5## To set a default target, run:## ln -sf /lib/systemd/system/<target name>.target /etc/systemd/system/default.target
這說明,在systemd掌權後,inittab不再起作用,也沒有了“運行級”的概念。現在起作用的設定檔是/etc/systemd/system/default.target這個檔案了。此檔案的內容如下:
# This file is part of systemd. # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. [Unit] Description=Multi-User System Documentation=man:systemd.special(7) Requires=basic.target Conflicts=rescue.service rescue.target After=basic.target rescue.service rescue.target AllowIsolate=yes [Install] 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、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 節點,或者是加密盤等,需要耗費較長的時間才可以準備就緒的情況下,因為並發啟動,這段時間內,系統並不是完全無事可做,而是可以利用這段空餘時間做更多的啟動進程的事情,總的來說就縮短了系統啟動時間。
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 SysteDefaultDependencies=noConditionPathExists=/sys/kernel/debugBefore=sysinit.target[Mount]What=debugfsWhere=/sys/kernel/debugType=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 有個很棒的承諾:介面保持穩定,不會再輕易改動。對於軟體開發人員來說,這是多麼體貼又讓人感動的承諾啊!
CentOS 7 巨大變動之 systemd 取代 SysV的Init