Linux 操做系統的啓動首先從 BIOS 開始,接下來進入 boot loader,由 bootloader 載入內核,進行內核初始化。內核初始化的最後一步就是啓動 pid 爲 1 的 init 進程。這個進程是系統的第一個進程。它負責產生其餘全部用戶進程。html
init 以守護進程方式存在,是全部其餘進程的祖先。init 進程很是獨特,可以完成其餘進程沒法完成的任務。mysql
Init 系統可以定義、管理和控制 init 進程的行爲。它負責組織和運行許多獨立的或相關的始化工做(所以被稱爲 init 系統),從而讓計算機系統進入某種用戶預訂的運行模式。linux
僅僅將內核運行起來是毫無實際用途的,必須由 init 系統將系統代入可操做狀態。好比啓動外殼 shell 後,便有了人機交互,這樣就可讓計算機執行一些預訂程序完成有實際意義的任務。或者啓動 X 圖形系統以便提供更佳的人機界面,更加高效的完成任務。這裏,字符界面的 shell 或者 X 系統都是一種預設的運行模式。程序員
大多數 Linux 發行版的 init 系統是和 System V 相兼容的,被稱爲 sysvinit。這是人們最熟悉的 init 系統。一些發行版如 Slackware 採用的是 BSD 風格 Init 系統,這種風格使用較少,本文再也不涉及。其餘的發行版如 Gentoo 是本身定製的。Ubuntu 和 RHEL 採用 upstart 替代了傳統的 sysvinit。而 Fedora 從版本 15 開始使用了一個被稱爲 systemd 的新 init 系統。sql
能夠看到不一樣的發行版採用了不一樣的 init 實現,本系列文章就是打算講述三個主要的 Init 系統:sysvinit,UpStart 和 systemd。瞭解它們各自的設計特色,並簡要介紹它們的使用。shell
在 Linux 主要應用於服務器和 PC 機的時代,SysVinit 運行很是良好,概念簡單清晰。它主要依賴於 Shell 腳本,這就決定了它的最大弱點:啓動太慢。在不多從新啓動的 Server 上,這個缺點並不重要。而當 Linux 被應用到移動終端設備的時候,啓動慢就成了一個大問題。爲了更快地啓動,人們開始改進 sysvinit,前後出現了 upstart 和 systemd 這兩個主要的新一代 init 系統。Upstart 已經開發了 8 年多,在很多系統中已經替換 sysvinit。Systemd 出現較晚,但發展更快,大有取代 upstart 的趨勢。數據庫
2、Sysvinit 概況apache
sysvinit 就是 system V 風格的 init 系統,顧名思義,它源於 System V 系列 UNIX。它提供了比 BSD 風格 init 系統更高的靈活性。是已經風行了幾十年的 UNIX init 系統,一直被各種 Linux 發行版所採用。編程
Sysvinit 用術語 runlevel 來定義"預訂的運行模式"。Sysvinit 檢查 '/etc/inittab' 文件中是否含有 'initdefault' 項。 這告訴 init 系統是否有一個默認運行模式。若是沒有默認的運行模式,那麼用戶將進入系統控制檯,手動決定進入何種運行模式。ubuntu
sysvinit 中運行模式描述了系統各類預訂的運行模式。一般會有 8 種運行模式,即運行模式 0 到 6 和 S 或者 s。
每種 Linux 發行版對運行模式的定義都不太同樣。但 0,1,6 卻獲得了你們的一致贊同:
0 關機
1 單用戶模式
6 重啓
一般在 /etc/inittab 文件中定義了各類運行模式的工做範圍。好比 RedHat 定義了 runlevel 3 和 5。運行模式 3 將系統初始化爲字符界面的 shell 模式;運行模式 5 將系統初始化爲 GUI 模式。不管是命令行界面仍是 GUI,運行模式 3 和 5 相對於其餘運行模式而言都是完整的正式的運行狀態,計算機能夠完成用戶須要的任務。而模式 1,S 等每每用於系統故障以後的排錯和恢復。
很顯然,這些不一樣的運行模式下系統須要初始化運行的進程和須要進行的初始化準備都是不一樣的。好比運行模式 3 不須要啓動 X 系統。用戶只須要指定須要進入哪一種模式,sysvinit 將負責執行全部該模式所必須的初始化工做。
Sysvinit 巧妙地用腳本,文件命名規則和軟連接來實現不一樣的 runlevel。首先,sysvinit 須要讀取/etc/inittab 文件。分析這個文件的內容,它得到如下一些配置信息:
系統須要進入的 runlevel
捕獲組合鍵的定義
定義電源 fail/restore 腳本
啓動 getty 和虛擬控制檯
獲得配置信息後,sysvinit 順序地執行如下這些步驟,從而將系統初始化爲預訂的 runlevel X。
/etc/rc.d/rc.sysinit
/etc/rc.d/rc 和/etc/rc.d/rcX.d/ (X 表明運行級別 0-6)
/etc/rc.d/rc.local
X Display Manager(若是須要的話)
首先,運行 rc.sysinit 以便執行一些重要的系統初始化任務。在 RedHat 公司的 RHEL5 中(RHEL6 已經使用 upstart 了),rc.sysinit 主要完成如下這些工做。
激活 udev 和 selinux
設置定義在/etc/sysctl.conf 中的內核參數
設置系統時鐘
加載 keymaps
使能交換分區
設置主機名(hostname)
根分區檢查和 remount
激活 RAID 和 LVM 設備
開啓磁盤配額
檢查並掛載全部文件系統
清除過時的 locks 和 PID 文件
完成了以上這些工做以後,sysvinit 開始運行/etc/rc.d/rc 腳本。根據不一樣的 runlevel,rc 腳本將打開對應該 runlevel 的 rcX.d 目錄(X 就是 runlevel),找到並運行存放在該目錄下的全部啓動腳本。每一個 runlevel X 都有一個這樣的目錄,目錄名爲/etc/rc.d/rcX.d。
在這些目錄下存放着不少不一樣的腳本。文件名以 S 開頭的腳本就是啓動時應該運行的腳本,S 後面跟的數字定義了這些腳本的執行順序。在/etc/rc.d/rcX.d 目錄下的腳本其實都是一些軟連接文件,真實的腳本文件存放在/etc/init.d 目錄下。以下所示:
[root@www ~]# ll /etc/rc5.d/ lrwxrwxrwx 1 root root 16 Sep 4 2008 K02dhcdbd -> ../init.d/dhcdbd ....(中間省略).... lrwxrwxrwx 1 root root 14 Sep 4 2008 K91capi -> ../init.d/capi lrwxrwxrwx 1 root root 23 Sep 4 2008 S00microcode_ctl -> ../init.d/microcode_ctl lrwxrwxrwx 1 root root 22 Sep 4 2008 S02lvm2-monitor -> ../init.d/lvm2-monitor ....(中間省略).... lrwxrwxrwx 1 root root 17 Sep 4 2008 S10network -> ../init.d/network ....(中間省略).... lrwxrwxrwx 1 root root 11 Sep 4 2008 S99local -> ../rc.local lrwxrwxrwx 1 root root 16 Sep 4 2008 S99smartd -> ../init.d/smartd ....(底下省略)....
當全部的初始化腳本執行完畢。Sysvinit 運行/etc/rc.d/rc.local 腳本。
rc.local 是 Linux 留給用戶進行個性化設置的地方。您能夠把本身私人想設置和啓動的東西放到這裏,一臺 Linux Server 的用戶通常不止一個,因此纔有這樣的考慮。
Sysvinit 不只須要負責初始化系統,還須要負責關閉系統。在系統關閉時,爲了保證數據的一致性,須要當心地按順序進行結束和清理工做。
好比應該先中止對文件系統有讀寫操做的服務,而後再 umount 文件系統。不然數據就會丟失。
這種順序的控制這也是依靠/etc/rc.d/rcX.d/目錄下全部腳本的命名規則來控制的,在該目錄下全部以 K 開頭的腳本都將在關閉系統時調用,字母 K 以後的數字定義了它們的執行順序。
這些腳本負責安全地中止服務或者其餘的關閉工做。
此外,在系統啓動以後,管理員還須要對已經啓動的進程進行管理和控制。原始的 sysvinit 軟件包包含了一系列的控制啓動,運行和關閉全部其餘程序的工具。
halt
中止系統。
init
這個就是 sysvinit 自己的 init 進程實體,以 pid1 身份運行,是全部用戶進程的父進程。最主要的做用是在啓動過程當中使用/etc/inittab 文件建立進程。
killall5
就是 SystemV 的 killall 命令。向除本身的會話(session)進程以外的其它進程發出信號,因此不能殺死當前使用的 shell。
last
回溯/var/log/wtmp 文件(或者-f 選項指定的文件),顯示自從這個文件創建以來,全部用戶的登陸狀況。
lastb
做用和 last 差很少,默認狀況下使用/var/log/btmp 文件,顯示全部失敗登陸企圖。
mesg
控制其它用戶對用戶終端的訪問。
pidof
找出程序的進程識別號(pid),輸出到標準輸出設備。
poweroff
等於 shutdown -h –p,或者 telinit 0。關閉系統並切斷電源。
reboot
等於 shutdown –r 或者 telinit 6。重啓系統。
runlevel
讀取系統的登陸記錄文件(通常是/var/run/utmp)把之前和當前的系統運行級輸出到標準輸出設備。
shutdown
以一種安全的方式終止系統,全部正在登陸的用戶都會收到系統將要終止通知,而且不許新的登陸。
sulogin
當系統進入單用戶模式時,被 init 調用。當接收到啓動加載程序傳遞的-b 選項時,init 也會調用 sulogin。
telinit
實際是 init 的一個鏈接,用來向 init 傳送單字符參數和信號。
utmpdump
以一種用戶友好的格式向標準輸出設備顯示/var/run/utmp 文件的內容。
wall
向全部有信息權限的登陸用戶發送消息。
不一樣的 Linux 發行版在這些 sysvinit 的基本工具基礎上又開發了一些輔助工具用來簡化 init 系統的管理工做。好比 RedHat 的 RHEL 在 sysvinit 的基礎上開發了 initscripts 軟件包,包含了大量的啓動腳本 (如 rc.sysinit) ,還提供了 service,chkconfig 等命令行工具,甚至一套圖形化界面來管理 init 系統。其餘的 Linux 發行版也有各自的 initscript 或其餘名字的 init 軟件包來簡化 sysvinit 的管理。
只要您理解了 sysvinit 的機制,在一個最簡的僅有 sysvinit 的系統下,您也能夠直接調用腳本啓動和中止服務,手動建立 inittab 和建立軟鏈接來完成這些任務。所以理解 sysvinit 的基本原理和命令是最重要的。您甚至也能夠開發本身的一套管理工具。
Sysvinit 的優勢是概念簡單。Service 開發人員只須要編寫啓動和中止腳本,概念很是清楚;將 service 添加/刪除到某個 runlevel 時,只須要執行一些建立/刪除軟鏈接文件的基本操做;這些都不須要學習額外的知識或特殊的定義語法(UpStart 和 Systemd 都須要用戶學習新的定義系統初始化行爲的語言)。
其次,sysvinit 的另外一個重要優勢是肯定的執行順序:腳本嚴格按照啓動數字的大小順序執行,一個執行完畢再執行下一個,這很是有益於錯誤排查。UpStart 和 systemd 支持併發啓動,致使沒有人能夠肯定地瞭解具體的啓動順序,排錯不易。
可是串行地執行腳本致使 sysvinit 運行效率較慢,在新的 IT 環境下,啓動快慢成爲一個重要問題。此外動態設備加載等 Linux 新特性也暴露出 sysvinit 設計的一些問題。針對這些問題,人們開始想辦法改進 sysvinit,以便加快啓動時間,並解決 sysvinit 自身的設計問題。
Upstart 是第一個被普遍應用的新一代 init 系統。咱們在接下來的第二部分介紹 UpStart。
假如您使用的 Linux 發行版是 Ubuntu,極可能會發如今您的計算機上找不到/etc/inittab 文件了,這是由於 Ubuntu 使用了一種被稱爲 upstart 的新型 init 系統。
大約在 2006 年或者更早的時候, Ubuntu 開發人員試圖將 Linux 安裝在筆記本電腦上。在這期間技術人員發現經典的 sysvinit 存在一些問題:它不適合筆記本環境。這促使程序員 Scott James Remnant 着手開發 upstart。
當 Linux 內核進入 2.6 時代時,內核功能有了不少新的更新。新特性使得 Linux 不只是一款優秀的服務器操做系統,也能夠被用於桌面系統,甚至嵌入式設備。桌面系統或便攜式設備的一個特色是常常重啓,並且要頻繁地使用硬件熱插拔技術。在現代計算機系統中,硬件繁多、接口有限,人們並不是將全部設備都始終鏈接在計算機上,好比 U 盤平時並不鏈接電腦,使用時才插入 USB 插口。所以,當系統上電啓動時,一些外設可能並無鏈接。而是在啓動後當須要的時候才鏈接這些設備。在 2.6 內核支持下,一旦新外設鏈接到系統,內核即可以自動實時地發現它們,並初始化這些設備,進而使用它們。這爲便攜式設備用戶提供了很大的靈活性。
但是這些特性爲 sysvinit 帶來了一些挑戰。當系統初始化時,須要被初始化的設備並無鏈接到系統上;好比打印機。爲了管理打印任務,系統須要啓動 CUPS 等服務,而若是打印機沒有接入系統的狀況下,啓動這些服務就是一種浪費。Sysvinit 沒有辦法處理這類需求,它必須一次性把全部可能用到的服務都啓動起來,即便打印機並無鏈接到系統,CUPS 服務也必須啓動。
還有網絡共享盤的掛載問題。在/etc/fstab 中,能夠指定系統自動掛載一個網絡盤,好比 NFS,或者 iSCSI 設備。在本文的第一部分 sysvinit 的簡介中能夠看到,sysvinit 分析/etc/fstab 掛載文件系統這個步驟是在網絡啓動以前。但是若是網絡沒有啓動,NFS 或者 iSCSI 都不可訪問,固然也沒法進行掛載操做。Sysvinit 採用 netdev 的方式來解決這個問題,即/etc/fstab 發現 netdev 屬性掛載點的時候,不嘗試掛載它,在網絡初始化並使能以後,還有一個專門的 netfs 服務來掛載全部這些網絡盤。這是一個不得已的補救方法,給管理員帶來不便。部分新手管理員甚至歷來也沒有據說過 netdev 選項,所以常常成爲系統管理的一個陷阱。
針對以上種種狀況,Ubuntu 開發人員在評估了當時的幾個可選 init 系統以後,決定從新設計和開發一個全新的 init 系統,即 UpStart。UpStart 基於事件機制,好比 U 盤插入 USB 接口後,udev 獲得內核通知,發現該設備,這就是一個新的事件。UpStart 在感知到該事件以後觸發相應的等待任務,好比處理/etc/fstab 中存在的掛載點。採用這種事件驅動的模式,upstart 完美地解決了即插即用設備帶來的新問題。
此外,採用事件驅動機制也帶來了一些其它有益的變化,好比加快了系統啓動時間。sysvinit 運行時是同步阻塞的。一個腳本運行的時候,後續腳本必須等待。這意味着全部的初始化步驟都是串行執行的,而實際上不少服務彼此並不相關,徹底能夠並行啓動,從而減少系統的啓動時間。在 Linux 大量應用於服務器的時代,系統啓動時間也許還不那麼重要;然而對於桌面系統和便攜式設備,啓動時間的長短對用戶體驗影響很大。此外雲計算等新的 Server 端技術也每每須要單個設備能夠更加快速地啓動。
UpStart 知足了這些需求,目前不只桌面系統 Ubuntu 採用了 UpStart,甚至企業級服務器級的 RHEL 也默認採用 UpStart 來替換 sysvinit 做爲 init 系統。
UpStart 解決了以前提到的 sysvinit 的缺點。採用事件驅動模型,UpStart 能夠:
更快地啓動系統
當新硬件被發現時動態啓動服務
硬件被拔除時動態中止服務
這些特色使得 UpStart 能夠很好地應用在桌面或者便攜式系統中,處理這些系統中的動態硬件插拔特性。
Upstart 的基本概念和設計清晰明確。UpStart 主要的概念是 job 和 event。Job 就是一個工做單元,用來完成一件工做,好比啓動一個後臺服務,或者運行一個配置命令。每一個 Job 都等待一個或多個事件,一旦事件發生,upstart 就觸發該 job 完成相應的工做。
Job 就是一個工做的單元,一個任務或者一個服務。能夠理解爲 sysvinit 中的一個服務腳本。有三種類型的工做:
task job;
service job;
abstract job;
task job 表明在必定時間內會執行完畢的任務,好比刪除一個文件;
service job 表明後臺服務進程,好比 apache httpd。這裏進程通常不會退出,一旦開始運行就成爲一個後臺精靈進程,由 init 進程管理,若是這類進程退出,由 init 進程從新啓動,它們只能由 init 進程發送信號中止。它們的中止通常也是因爲所依賴的中止事件而觸發的,不過 upstart 也提供命令行工具,讓管理人員手動中止某個服務;
Abstract job 僅由 upstart 內部使用,僅對理解 upstart 內部機理有所幫助。咱們不用關心它。
除了以上的分類以外,還有另外一種工做(Job)分類方法。Upstart 不只能夠用來爲整個系統的初始化服務,也能夠爲每一個用戶會話(session)的初始化服務。系統的初始化任務就叫作 system job,好比掛載文件系統的任務就是一個 system job;用戶會話的初始化服務就叫作 session job。
Upstart 爲每一個工做都維護一個生命週期。通常來講,工做有開始,運行和結束這幾種狀態。爲了更精細地描述工做的變化,Upstart 還引入了一些其它的狀態。好比開始就有開始以前(pre-start),即將開始(starting)和已經開始了(started)幾種不一樣的狀態,這樣能夠更加精確地描述工做的當前狀態。
工做從某種初始狀態開始,逐漸變化,或許要經歷其它幾種不一樣的狀態,最終進入另一種狀態,造成一個狀態機。在這個過程當中,當工做的狀態即將發生變化的時候,init 進程會發出相應的事件(event)。
狀態名 | 含義 |
---|---|
Waiting | 初始狀態 |
Starting | Job 即將開始 |
pre-start | 執行 pre-start 段,即任務開始前應該完成的工做 |
Spawned | 準備執行 script 或者 exec 段 |
post-start | 執行 post-start 動做 |
Running | interim state set after post-start section processed denoting job is running (But it may have no associated PID!) |
pre-stop | 執行 pre-stop 段 |
Stopping | interim state set after pre-stop section processed |
Killed | 任務即將被中止 |
post-stop | 執行 post-stop 段 |
圖 1 展現了 Job 的狀態機。
其中有四個狀態會引發 init 進程發送相應的事件,代表該工做的相應變化:
Starting
Started
Stopping
Stopped
而其它的狀態變化不會發出事件。那麼咱們接下來就來看看事件的詳細含義吧。
顧名思義,Event 就是一個事件。事件在 upstart 中以通知消息的形式具體存在。一旦某個事件發生了,Upstart 就向整個系統發送一個消息。沒有任何手段阻止事件消息被 upstart 的其它部分知曉,也就是說,事件一旦發生,整個 upstart 系統中全部工做和其它的事件都會獲得通知。
Event 能夠分爲三類: signal,methods 或者 hooks。
Signals
Signal 事件是非阻塞的,異步的。發送一個信號以後控制權當即返回。
Methods
Methods 事件是阻塞的,同步的。
Hooks
Hooks 事件是阻塞的,同步的。它介於 Signals 和 Methods 之間,調用發出 Hooks 事件的進程必須等待事件完成才能夠獲得控制權,但不檢查事件是否成功。
事件是個很是抽象的概念,下面我羅列出一些常見的事件,但願能夠幫助您進一步瞭解事件的含義:
系統上電啓動,init 進程會發送"start"事件
根文件系統可寫時,相應 job 會發送文件系統就緒的事件
一個塊設備被發現並初始化完成,發送相應的事件
某個文件系統被掛載,發送相應的事件
相似 atd 和 cron,能夠在某個時間點,或者週期的時間點發送事件
另一個 job 開始或結束時,發送相應的事件
一個磁盤文件被修改時,能夠發出相應的事件
一個網絡設備被發現時,能夠發出相應的事件
缺省路由被添加或刪除時,能夠發出相應的事件
不一樣的 Linux 發行版對 upstart 有不一樣的定製和實現,實現和支持的事件也有所不一樣,能夠用man 7 upstart-events
來查看事件列表。
Upstart 就是由事件觸發工做運行的一個系統,每個程序的運行都由其依賴的事件發生而觸發的。
系統初始化的過程是在工做和事件的相互協做下完成的,能夠大體描述以下:系統初始化時,init 進程開始運行,init 進程自身會發出不一樣的事件,這些最初的事件會觸發一些工做運行。每一個工做運行過程當中會釋放不一樣的事件,這些事件又將觸發新的工做運行。如此反覆,直到整個系統正常運行起來。
究竟哪些事件會觸發某個工做的運行?這是由工做配置文件定義的。
任何一個工做都是由一個工做配置文件(Job Configuration File)定義的。這個文件是一個文本文件,包含一個或者多個小節(stanza)。每一個小節是一個完整的定義模塊,定義了工做的一個方面,好比 author 小節定義了工做的做者。工做配置文件存放在/etc/init 下面,是以.conf 做爲文件後綴的文件。
#This is a simple demo of Job Configure file #This line is comment, start with # #Stanza 1, The author author 「Liu Ming」 #Stanza 2, Description description 「This job only has author and description, so no use, just a demo」
上面的例子不會產生任何做用,一個真正的工做配置文件會包含不少小節,其中比較重要的小節有如下幾個:
"expect" Stanza
Upstart 除了負責系統的啓動過程以外,和 SysVinit 同樣,Upstart 還提供一系列的管理工具。當系統啓動以後,管理員可能還須要進行維護和調整,好比啓動或者中止某項系統服務。或者將系統切換到其它的工做狀態,好比改變運行級別。本文後續將詳細介紹 Upstart 的管理工具的使用。
爲了啓動,中止,重啓和查詢某個系統服務。Upstart 須要跟蹤該服務所對應的進程。好比 httpd 服務的進程 PID 爲 1000。當用戶須要查詢 httpd 服務是否正常運行時,Upstart 就能夠利用 ps 命令查詢進程 1000,假如它還在正常運行,則代表服務正常。當用戶須要中止 httpd 服務時,Upstart 就使用 kill 命令終止該進程。爲此,Upstart 必須跟蹤服務進程的進程號。
部分服務進程爲了將本身變成後臺精靈進程(daemon),會採用兩次派生(fork)的技術,另一些服務則不會這樣作。假如一個服務派生了兩次,那麼 UpStart 必須採用第二個派生出來的進程號做爲服務的 PID。可是,UpStart 自己沒法判斷服務進程是否會派生兩次,爲此在定義該服務的工做配置文件中必須寫明 expect 小節,告訴 UpStart 進程是否會派生兩次。
Expect 有兩種,"expect fork"表示進程只會 fork 一次;"expect daemonize"表示進程會 fork 兩次。
"exec" Stanza 和"script" Stanza
一個 UpStart 工做必定須要作些什麼,多是運行一條 shell 命令,或者運行一段腳本。用"exec"關鍵字配置工做須要運行的命令;用"script"關鍵字定義須要運行的腳本。
清單 2 顯示了 exec 和 script 的用法:
# mountall.conf description 「Mount filesystems on boot」 start on startup stop on starting rcS ... script . /etc/default/rcS [ -f /forcefsck ] && force_fsck=」--force-fsck」 [ 「$FSCKFIX」=」yes」 ] && fsck_fix=」--fsck-fix」 ... exec mountall –daemon $force_fsck $fsck_fix end script ...
這是 mountall 的例子,該工做在系統啓動時運行,負責掛載全部的文件系統。該工做須要執行復雜的腳本,由"script"關鍵字定義;在腳本中,使用了 exec 來執行 mountall 命令。
"start on" Stanza 和"stop on" Stanza
"start on"定義了觸發工做的全部事件。"start on"的語法很簡單,以下所示:
start on EVENT [[KEY=]VALUE]... [and|or...]
EVENT 表示事件的名字,能夠在 start on 中指定多個事件,表示該工做的開始須要依賴多個事件發生。多個事件之間能夠用 and 或者 or 組合,"表示所有都必須發生"或者"其中之一發生便可"等不一樣的依賴條件。除了事件發生以外,工做的啓動還能夠依賴特定的條件,所以在 start on 的 EVENT 以後,能夠用 KEY=VALUE 來表示額外的條件,通常是某個環境變量(KEY)和特定值(VALUE)進行比較。若是隻有一個變量,或者變量的順序已知,則 KEY 能夠省略。
"stop on"和"start on"很是相似,只不過是定義工做在什麼狀況下須要中止。
代碼清單 3 是"start on"和"stop on"的一個例子。
#dbus.conf description 「D-Bus system message bus」 start on local-filesystems stop on deconfiguring-networking …
D-Bus 是一個系統消息服務,上面的配置文件代表當系統發出 local-filesystems 事件時啓動 D-Bus;當系統發出 deconfiguring-networking 事件時,中止 D-Bus 服務。
UpStart 還能夠用於管理用戶會話的初始化。在我寫這篇文章的今天,多數 Linux 發行版尚未使用 UpStart 管理會話。只有在 Ubuntu Raring 版本中,使用 UpStart 管理用戶會話的初始化過程。
首先讓咱們瞭解一下 Session 的概念。Session 就是一個用戶會話,即用戶從遠程或者本地登入系統開始工做,直到用戶退出。這整個過程就構成一個會話。
每一個用戶的使用習慣和使用方法都不相同,所以用戶每每須要爲本身的會話作一個定製,好比添加特定的命令別名,啓動特殊的應用程序或者服務,等等。這些工做都屬於對特定會話的初始化操做,所以能夠被稱爲 Session Init。
用戶使用 Linux 能夠有兩種模式:字符模式和圖形界面。在字符模式下,會話初始化相對簡單。用戶登陸後只能啓動一個 Shell,經過 shell 命令使用系統。各類 shell 程序都支持一個自動運行的啓動腳本,好比~/.bashrc。用戶在這些腳本中加入須要運行的定製化命令。字符會話需求簡單,所以這種現有的機制工做的很好。
在圖形界面下,事情就變得複雜一些。用戶登陸後看到的並非一個 shell 提示符,而是一個桌面。一個完整的桌面環境由不少組件組成。
一個桌面環境包括 window manager,panel 以及其它一些定義在/usr/share/gnome-session/sessions/下面的基本組件;此外還有一些輔助的應用程序,共同幫助構成一個完整的方便的桌面,好比 system monitors,panel applets,NetworkManager,Bluetooth,printers 等。當用戶登陸以後,這些組件都須要被初始化,這個過程比字符界面要複雜的多。目前啓動各類圖形組件和應用的工做由 gnome-session 完成。過程以下:
以 Ubuntu 爲例,當用戶登陸 Ubuntu 圖形界面後,顯示管理器(Display Manager)lightDM 啓動 Xsession。Xsession 接着啓動 gnome-session,gnome-session 負責其它的初始化工做,而後就開始了一個 desktop session。
init |- lightdm | |- Xorg | |- lightdm ---session-child | |- gnome-session --session=ubuntu | |- compiz | |- gwibber | |- nautilus | |- nm-applet | : | : | |- dbus-daemon --session | : :
這個過程有一些缺點(和 sysVInit 相似)。一些應用和組件其實並不須要在會話初始化過程當中啓動,更好的選擇是在須要它們的時候才啓動。好比 update-notifier 服務,該服務不停地監測幾個文件系統路徑,一旦這些路徑上發現能夠更新的軟件包,就提醒用戶。這些文件系統路徑包括新插入的 DVD 盤等。Update-notifier 由 gnome-session 啓動並一直運行着,在多數狀況下,用戶並不會插入新的 DVD,此時 update-notifier 服務一直在後臺運行並消耗系統資源。更好的模式是當用戶插入 DVD 的時候再運行 update-notifier。這樣能夠加快啓動時間,減少系統運行過程當中的內存等系統資源的開銷。對於移動,嵌入式等設備等這還意味着省電。除了 Update-notifier 服務以外,還有其它一些相似的服務。好比 Network Manager,一天以內用戶不多切換網絡設備,因此大部分時間 Network Manager 服務僅僅是在浪費系統資源;再好比 backup manager 等其它常駐內存,後臺不間斷運行卻不多真正被使用的服務。
用 UpStart 的基於事件的按需啓動的模式就能夠很好地解決這些問題,好比用戶插入網線的時候才啓動 Network Manager,由於用戶插入網線代表須要使用網絡,這能夠被稱爲按需啓動。
下圖描述了採用 UpStart 以後的會話初始化過程。
init |- lightdm | |- Xorg | |- lightdm ---session-child | |- session-init # <-- upstart running as normal user | |- dbus-daemon --session | |- gnome-session --session=ubuntu | |- compiz | |- gwibber | |- nautilus | |- nm-applet | : | : : :
有兩種人員須要瞭解 Upstart 的使用。第一類是系統開發人員,好比 MySQL 的開發人員。它們須要瞭解如何編寫工做配置文件,以便用 UpStart 來管理服務。好比啓動,中止 MySQL 服務。
另一種狀況是系統管理員,它們須要掌握 Upstart 的管理命令以便配置和管理系統的初始化,管理系統服務。
系統開發人員不只須要掌握工做配置文件的寫法,還須要瞭解一些針對服務進程編程上的要求。本文僅列出了少數工做配置文件的語法。要全面掌握工做配置文件的寫法,須要詳細閱讀 Upstart 的手冊。這裏讓咱們來分析一下如何用 Upstart 來實現傳統的運行級別,進而瞭解如何靈活使用工做配置文件。
Upstart 系統中的運行級別
Upstart 的運做徹底是基於工做和事件的。工做的狀態變化和運行會引發事件,進而觸發其它工做和事件。
而傳統的 Linux 系統初始化是基於運行級別的,即 SysVInit。由於歷史的緣由,Linux 上的多數軟件仍是採用傳統的 SysVInit 腳本啓動方式,並無爲 UpStart 開發新的啓動腳本,所以即使在 Debian 和 Ubuntu 系統上,仍是必須模擬老的 SysVInit 的運行級別模式,以便和多數現有軟件兼容。
雖然 Upstart 自己並無運行級別的概念,但徹底能夠用 UpStart 的工做模擬出來。讓咱們完整地考察一下 UpStart 機制下的系統啓動過程。
系統啓動過程
下圖描述了 UpStart 的啓動過程。
系統上電後運行 GRUB 載入內核。內核執行硬件初始化和內核自身初始化。在內核初始化的最後,內核將啓動 pid 爲 1 的 init 進程,即 UpStart 進程。
Upstart 進程在執行了一些自身的初始化工做後,當即發出"startup"事件。上圖中用紅色方框加紅色箭頭表示事件,能夠在左上方看到"startup"事件。
全部依賴於"startup"事件的工做被觸發,其中最重要的是 mountall。mountall 任務負責掛載系統中須要使用的文件系統,完成相應工做後,mountall 任務會發出如下事件:local-filesystem,virtual-filesystem,all-swaps,
其中 virtual-filesystem 事件觸發 udev 任務開始工做。任務 udev 觸發 upstart-udev-bridge 的工做。Upstart-udev-bridge 會發出 net-device-up IFACE=lo 事件,表示本地迴環 IP 網絡已經準備就緒。同時,任務 mountall 繼續執行,最終會發出 filesystem 事件。
此時,任務 rc-sysinit 會被觸發,由於 rc-sysinit 的 start on 條件以下:
start on filesystem and net-device-up IFACE=lo
任務 rc-sysinit 調用 telinit。Telinit 任務會發出 runlevel 事件,觸發執行/etc/init/rc.conf。
rc.conf 執行/etc/rc$.d/目錄下的全部腳本,和 SysVInit 很是相似,讀者能夠參考本文第一部分的描述。
程序開發時須要注意的事項
做爲程序開發人員,在編寫系統服務時,須要瞭解 UpStart 的一些特殊要求。只有符合這些要求的軟件才能夠被 UpStart 管理。
規則一,派生次數需聲明。
不少 Linux 後臺服務都經過派生兩次的技巧將本身變成後臺服務程序。若是您編寫的服務也採用了這個技術,就必須經過文檔或其它的某種方式明確地讓 UpStart 的維護人員知道這一點,這將影響 UpStart 的 expect stanza,咱們在前面已經詳細介紹過這個 stanza 的含義。
規則二,派生後便可用。
後臺程序在完成第二次派生的時候,必須保證服務已經可用。由於 UpStart 經過派生計數來決定服務是否處於就緒狀態。
規則三,遵照 SIGHUP 的要求。
UpStart 會給精靈進程發送 SIGHUP 信號,此時,UpStart 但願該精靈進程作如下這些響應工做:
完成全部必要的從新初始化工做,好比從新讀取配置文件。這是由於 UpStart 的命令"initctl reload"被設計爲可讓服務在不重啓的狀況下更新配置。
精靈進程必須繼續使用現有的 PID,即收到 SIGHUP 時不能調用 fork。若是服務必須在這裏調用 fork,則等同於派生兩次,參考上面的規則一的處理。這個規則保證了 UpStart 能夠繼續使用 PID 管理本服務。
規則四,收到 SIGTEM 即 shutdown。
當收到 SIGTERM 信號後,UpStart 但願精靈進程進程當即乾淨地退出,釋放全部資源。若是一個進程在收到 SIGTERM 信號後不退出,Upstart 將對其發送 SIGKILL 信號。
做爲系統管理員,一個重要的職責就是管理系統服務。好比系統服務的監控,啓動,中止和配置。UpStart 提供了一系列的命令來完成這些工做。其中的核心是initctl,這是一個帶子命令風格的命令行工具。
好比能夠用 initctl list 來查看全部工做的概況:
$initctl list alsa-mixer-save stop/waiting avahi-daemon start/running, process 690 mountall-net stop/waiting rc stop/waiting rsyslog start/running, process 482 screen-cleanup stop/waiting tty4 start/running, process 859 udev start/running, process 334 upstart-udev-bridge start/running, process 304 ureadahead-other stop/waiting
這是在 Ubuntu10.10 系統上的輸出,其它的 Linux 發行版上的輸出會有所不一樣。第一列是工做名,好比 rsyslog。第二列是工做的目標;第三列是工做的狀態。
此外還能夠用 initctl stop 中止一個正在運行的工做;用 initctl start 開始一個工做;還能夠用 initctl status 來查看一個工做的狀態;initctl restart 重啓一個工做;initctl reload 可讓一個正在運行的服務從新載入配置文件。這些命令和傳統的 service 命令十分類似。
Service 命令 | UpStart initctl 命令 |
---|---|
service start | initctl start |
service stop | initctl stop |
service restart | initctl restart |
service reload | initctl reload |
不少狀況下管理員並不喜歡子命令風格,由於須要手動鍵入的字符太多。UpStart 還提供了一些快捷命令來簡化 initctl,實際上這些命令只是在內部調用相應的 initctl 命令。好比 reload,restart,start,stop 等等。啓動一個服務能夠簡單地調用
start
這和執行 initctl start是同樣的效果。
一些命令是爲了兼容其它系統(主要是 sysvinit),好比顯示 runlevel 用/sbin/runlevel 命令:
$runlevel N 2
這個輸出說明當前系統的運行級別爲 2。並且系統沒有以前的運行級別,也就是說在系統上電啓動進入預約運行級別以後沒有再修改過運行級別。
那麼如何修改系統上電以後的默認運行級別呢?
在 Upstart 系統中,須要修改/etc/init/rc-sysinti.conf 中的 DEFAULT_RUNLEVEL 這個參數,以便修改默認啓動運行級別。這一點和 sysvinit 的習慣有所不一樣,你們須要格外留意。
還有一些隨 UpStart 發佈的小工具,用來幫助開發 UpStart 或者診斷 UpStart 的問題。好比 init-checkconf 和 upstart-monitor
還能夠使用 initctl 的 emit 命令從命令行發送一個事件。
#initctl emit
這通常是用於 UpStart 自己的排錯。
能夠看到,UpStart 的設計比 SysVInit 更加先進。多數 Linux 發行版上已經再也不使用 SysVInit,一部分發行版採用了 UpStart,好比 Ubuntu;而另一些好比 Fedora,採用了一種被稱爲 systemd 的 init 系統。Systemd 出現的比 UpStart 更晚,但發展迅速,雖然 UpStart 也還在積極開發並被愈來愈多地應用,但 systemd 彷佛發展更快,我將在下一篇文章中再介紹 systemd。
Systemd 是 Linux 系統中最新的初始化系統(init),它主要的設計目標是克服 sysvinit 固有的缺點,提升系統的啓動速度。systemd 和 ubuntu 的 upstart 是競爭對手,預計會取代 UpStart,實際上在做者寫做本文時,已經有消息稱 Ubuntu 也將採用 systemd 做爲其標準的系統初始化系統。
Systemd 的不少概念來源於蘋果 Mac OS 操做系統上的 launchd,不過 launchd 專用於蘋果系統,所以長期未能得到應有的普遍關注。Systemd 借鑑了不少 launchd 的思想,它的重要特性以下:
Systemd 是一個"新來的",Linux 上的不少應用程序並無來得及爲它作相應的改變。和 UpStart 同樣,systemd 引入了新的配置方式,對應用程序的開發也有一些新的要求。若是 systemd 想替代目前正在運行的初始化系統,就必須和現有程序兼容。任何一個 Linux 發行版都很難爲了採用 systemd 而在短期內將全部的服務代碼都修改一遍。
Systemd 提供了和 Sysvinit 以及 LSB initscripts 兼容的特性。系統中已經存在的服務和進程無需修改。這下降了系統向 systemd 遷移的成本,使得 systemd 替換現有初始化系統成爲可能。
Systemd 提供了比 UpStart 更激進的並行啓動能力,採用了 socket / D-Bus activation 等技術啓動服務。一個顯而易見的結果就是:更快的啓動速度。
爲了減小系統啓動時間,systemd 的目標是:
儘量啓動更少的進程
儘量將更多進程並行啓動
一樣地,UpStart 也試圖實現這兩個目標。UpStart 採用事件驅動機制,服務能夠暫不啓動,當須要的時候才經過事件觸發其啓動,這符合第一個設計目標;此外,不相干的服務能夠並行啓動,這也實現了第二個目標。
下面的圖形演示了 UpStart 相對於 SysVInit 在併發啓動這個方面的改進:
假設有 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 也能夠併發啓動。從而實現以下圖所示的併發啓動過程:
全部的任務都同時併發執行,總的啓動時間被進一步下降爲 T1。
可見 systemd 比 UpStart 更進一步提升了並行啓動能力,極大地加速了系統啓動時間。
當 sysvinit 系統初始化的時候,它會將全部可能用到的後臺服務進程所有啓動運行。而且系統必須等待全部的服務都啓動就緒以後,才容許用戶登陸。這種作法有兩個缺點:首先是啓動時間過長;其次是系統資源浪費。
某些服務極可能在很長一段時間內,甚至整個服務器運行期間都沒有被使用過。好比 CUPS,打印服務在多數服務器上不多被真正使用到。您可能沒有想到,在不少服務器上 SSHD 也是不多被真正訪問到的。花費在啓動這些服務上的時間是沒必要要的;一樣,花費在這些服務上的系統資源也是一種浪費。
Systemd 能夠提供按需啓動的能力,只有在某個服務被真正請求的時候才啓動它。當該服務結束,systemd 能夠關閉它,等待下次須要時再次啓動它。
init 系統的一個重要職責就是負責跟蹤和管理服務進程的生命週期。它不只能夠啓動一個服務,也必須也可以中止服務。這看上去沒有什麼特別的,然而在真正用代碼實現的時候,您或許會發現中止服務比一開始想的要困難。
服務進程通常都會做爲精靈進程(daemon)在後臺運行,爲此服務程序有時候會派生(fork)兩次。在 UpStart 中,須要在配置文件中正確地配置 expect 小節。這樣 UpStart 經過對 fork 系統調用進行計數,從而獲知真正的精靈進程的 PID 號。好比圖 3 所示的例子:
若是 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 內容時,才臨時執行掛載以便訪問其中的內容,而不訪問光盤時該掛載點被取消(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 將不一樣的信息保存到不一樣的文件中,分析的時候很難肯定哪些條目是相關的。
擴展性:日誌的適用範圍很廣,從嵌入式設備到超級計算機集羣均可以知足需求。
安全性:日誌 文件是能夠驗證的,讓沒法檢測的修改再也不可能。
系統初始化須要作的事情很是多。須要啓動後臺服務,好比啓動 SSHD 服務;須要作配置工做,好比掛載文件系統。這個過程當中的每一步都被 systemd 抽象爲一個配置單元,即 unit。能夠認爲一個服務是一個配置單元;一個掛載點是一個配置單元;一個交換分區的配置是一個配置單元;等等。systemd 將配置單元概括爲如下一些不一樣的類型。然而,systemd 正在快速發展,新功能不斷增長。因此配置單元類型可能在不久的未來繼續增長。
service :表明一個後臺服務進程,好比 mysqld。這是最經常使用的一類。
socket :此類配置單元封裝系統和互聯網中的一個 套接字 。當下,systemd 支持流式、數據報和連續包的 AF_INET、AF_INET六、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 的事務概念和數據庫中的有所不一樣,主要是爲了保證多個依賴的配置單元之間沒有環形引用。好比 unit A、B、C,假如它們的依賴關係爲:
存在循環依賴,那麼 systemd 將沒法啓動任意一個服務。此時 systemd 將會嘗試解決這個問題,由於配置單元之間的依賴關係有兩種:required 是強依賴;want 則是弱依賴,systemd 將去掉 wants 關鍵字指定的依賴看看是否能打破循環。若是沒法修復,systemd 會報錯。
Systemd 可以自動檢測和修復這類配置錯誤,極大地減輕了管理員的排錯負擔。
systemd 用目標(target)替代了運行級別的概念,提供了更大的靈活性,如您能夠繼承一個已有的目標,並添加其它服務,來建立本身的目標。下表列舉了 systemd 下的目標和常見 runlevel 的對應關係:
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 又須要記錄日誌,這豈不是會出現問題?
Systemd 的開發人員仔細研究了服務之間相互依賴的本質問題,發現所謂依賴能夠分爲三個具體的類型,而每個類型實際上均可以經過相應的技術解除依賴關係。
絕大多數的服務依賴是套接字依賴。好比服務 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 是 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 管理。這須要您注意如下這些要點:
後臺服務進程代碼不須要執行兩次派生來實現後臺精靈進程,只須要實現服務自己的主循環便可。
不要調用 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 和傳統的系統管理命令會很是清晰。
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 的替代,它能夠:
維護,跟蹤會話和用戶登陸狀況。如上所述,爲了決定關機命令是否可行,系統須要瞭解當前用戶登陸狀況,若是用戶從 SSH 登陸,不容許其執行關機命令;若是普通用戶從本地登陸,且該用戶是系統中的惟一會話,則容許其執行關機命令;這些判斷都須要 logind 維護全部的用戶會話和登陸狀況。
Logind 也負責統計用戶會話是否長時間沒有操做,能夠執行休眠/關機等相應操做。
爲用戶會話的全部進程建立 CGroup。這不只方便統計全部用戶會話的相關進程,也能夠實現會話級別的系統資源控制。
負責電源管理的組合鍵處理,好比用戶按下電源鍵,將系統切換至睡眠狀態。
多席位(multi-seat) 管理。現在的電腦,即使一臺筆記本電腦,也徹底能夠提供多人同時使用的計算能力。多席位就是一臺電腦主機管理多個外設,好比兩個屏幕和兩個鼠標/鍵盤。席位一使用屏幕 1 和鍵盤 1;席位二使用屏幕 2 和鍵盤 2,但他們都共享一臺主機。用戶會話能夠自由在多個席位之間切換。或者當插入新的鍵盤,屏幕等物理外設時,自動啓動 gdm 用戶登陸界面等。全部這些都是多席位管理的內容。ConsoleKit 始終沒有實現這個功能,systemd 的 logind 可以支持多席位。
以上描述的這些管理功能僅僅是 systemd 的部分功能,除此以外,systemd 還負責系統其餘的管理配置,好比配置網絡,Locale 管理,管理系統內核模塊加載等,完整地描述它們已經超出了本人的能力。
在不才做者看來,做爲系統初始化系統,systemd 的最大特色有兩個:
使人驚奇的激進的併發啓動能力,極大地提升了系統啓動速度;
用 CGroup 統計跟蹤子進程,乾淨可靠。
此外,和其前任不一樣的地方在於,systemd 已經不只僅是一個初始化系統了。
Systemd 出色地替代了 sysvinit 的全部功能,但它並未就此自滿。由於 init 進程是系統全部進程的父進程這樣的特殊性,systemd 很是適合提供曾經由其餘服務提供的功能,好比定時任務 (之前由 crond 完成) ;會話管理 (之前由 ConsoleKit/PolKit 等管理) 。僅僅從本文皮毛同樣的介紹來看,Systemd 已經管得不少了,可它還在不斷髮展。它將逐漸成爲一個多功能的系統環境,可以處理很是多的系統管理任務,有人甚至將它看做一個操做系統。
好的一點是,這很是有助於標準化 Linux 的管理!從前,不一樣的 Linux 發行版各行其事,使用不一樣方法管理系統,歷來也不會互相妥協。好比如何將系統進入休眠狀態,不一樣的系統有不一樣的解決方案,即使是同一個 Linux 系統,也存在不一樣的方法,好比一個有趣的討論:如何讓 ubuntu 系統休眠,能夠使用底層的/sys/power/state 接口,也能夠使用諸如 pm-utility 等高層接口。存在這麼多種不一樣的方法作一件事情對像我這樣的普通用戶而言可不是件有趣的事情。systemd 提供統一的電源管理命令接口,這件事情的意義就相似全世界的人都說統一的語言,咱們不再須要學習外語了,多麼美好!
若是全部的 Linux 發行版都採納了 systemd,那麼系統管理任務即可以很大程度上實現標準化。此外 systemd 有個很棒的承諾:接口保持穩定,不會再輕易改動。對於軟件開發人員來講,這是多麼體貼又讓人感動的承諾啊!
本文摘自https://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html