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、X十一、HAL 能夠同時啓動。乍一看,這彷佛有點兒問題,好比 Avahi 須要 syslog 的服務,Avahi 和 syslog 同時啓動,假設 Avahi 的啓動比較快,因此 syslog 尚未準備好,但是 Avahi 又須要記錄日誌,這豈不是會出現問題? mysql
Systemd 的開發人員仔細研究了服務之間相互依賴的本質問題,發現所謂依賴能夠分爲三個具體的類型,而每個類型實際上均可以經過相應的技術解除依賴關係。 redis
絕大多數的服務依賴是套接字依賴。好比服務 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 是 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 節點,或者是加密盤等,須要耗費較長的時間才能夠準備就緒的狀況下,由於併發啓動,這段時間內,系統並非徹底無事可作,而是能夠利用這段空餘時間作更多的啓動進程的事情,總的來講就縮短了系統啓動時間。
開發人員須要瞭解 systemd 的更多細節。好比您打算開發一個新的系統服務,就必須瞭解如何讓這個服務可以被 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 等文檔進行深刻學習。但願經過上面幾個小例子,你們已經瞭解配置單元文件的做用和通常寫法了。
多數管理員應該都已經很是熟悉系統服務和 init 系統的管理,好比 service、chkconfig 以及 telinit 命令的使用。systemd 也完成一樣的管理任務,只是命令工具 systemctl 的語法有所不一樣而已,所以用表格來對比 systemctl 和傳統的系統管理命令會很是清晰。
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 如何處理電源管理,命令以下表所示:
命令 | 操做 |
---|---|
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 的替代,它能夠:
在不才做者看來,做爲系統初始化系統,systemd 的最大特色有兩個:
此外,和其前任不一樣的地方在於,systemd 已經不只僅是一個初始化系統了。
Systemd 出色地替代了 sysvinit 的全部功能,但它並未就此自滿。由於 init 進程是系統全部進程的父進程這樣的特殊性,systemd 很是適合提供曾經由其餘服務提供的功能,好比定時任務 (之前由 crond 完成) ;會話管理 (之前由 ConsoleKit/PolKit 等管理) 。僅僅從本文皮毛同樣的介紹來看,Systemd 已經管得不少了,可它還在不斷髮展。它將逐漸成爲一個多功能的系統環境,可以處理很是多的系統管理任務,有人甚至將它看做一個操做系統。
好的一點是,這很是有助於標準化 Linux 的管理!從前,不一樣的 Linux 發行版各行其事,使用不一樣方法管理系統,歷來也不會互相妥協。好比如何將系統進入休眠狀態,不一樣的系統有不一樣的解決方案,即使是同一個 Linux 系統,也存在不一樣的方法,好比一個有趣的討論:如何讓 ubuntu 系統休眠,可使用底層的/sys/power/state 接口,也可使用諸如 pm-utility 等高層接口。存在這麼多種不一樣的方法作一件事情對像我這樣的普通用戶而言可不是件有趣的事情。systemd 提供統一的電源管理命令接口,這件事情的意義就相似全世界的人都說統一的語言,咱們不再須要學習外語了,多麼美好!
若是全部的 Linux 發行版都採納了 systemd,那麼系統管理任務即可以很大程度上實現標準化。此外 systemd 有個很棒的承諾:接口保持穩定,不會再輕易改動。對於軟件開發人員來講,這是多麼體貼又讓人感動的承諾啊!