轉自:http://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html, 少有改動。
1 systemd是什麼
2 Systemd物理文件組成
- # 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
- # 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
3 Systemd運行原理
3.1 systemd的基本概念
(1)配置單元unit
- service 後代服務進程,如httpd,mysqld等
- soket 對應一個套接字,之後對應到一個service,類似於xinetd的功能
- device 對應udev規則標記的一個設備
- mount 系統中的一個掛載點,systemd據此進行自動掛載,爲了與SystemV兼容,目前systemd自動處理/etc/fstab並轉化爲mount
- automount 自動掛載點
- swap 配置交換分區
- target 配置單元的邏輯分組,包含多個相關的配置單元,可以當成是SystemV中的運行級。
- timer 定時器。用來定時觸發用戶定義的操作,它可以用來取代傳統的atd,crond等。
- snapshot 與target類似,表示當前的運行狀態
(2)依賴關係
(3)Target和runlevel
表 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 |
如前所述,在 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 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 能夠支持多席位。
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 有個很棒的承諾:接口保持穩定,不會再輕易改動。對於軟件開發人員來說,這是多麼體貼又讓人感動的承諾啊!