Upstart: Ubuntu 的基於事件的啓動進程

Upstart: Ubuntu 的基於事件的啓動進程


在Ubuntu中,inittab軟件包已經被Upstart 軟件包替換了,所有的配置信息都在/etc/event.d/目錄下。

******************************************************************

By Mark Sobell on February 08, 2008 (9:00:00 AM)
原文鏈接:http://www.linux.com/feature/125977
翻譯時間:2008年3月7日
譯者:王旭(gnawux at gmail.com)

譯註:儘管譯者是一位鐵桿的Debian粉絲,但也注意 upstart 很久了,就譯者本人觀點,upstart 應該說是 Ubuntu 所做的衆多工作中最爲傑出的一個,它將可以極大地加快 Linux 系統啓動的過程。儘管它不是惟一的下一代 init 程序,但它已經作爲 Ubuntu 的缺省 init 進程工作了相當長的時間,這點將極大有助於程序的成熟;而且,upstart 使用了基於事件的模型,而不是簡單的將各個 daemon 並行化,這個架構上的突破也是具有革命性的,下面我們來看這篇文章吧。
因爲傳統的System V 的 init daemon (Sysvinit)無法很好地處理現代硬件,如熱插拔設備、USB硬盤或山村、網絡文件系統等,Ubuntu 使用了Upstart init daemon。

本文抽取自最近出版的 A Practical Guide to Ubuntu Linux .

目前已經有多種 sysvinit 的替代產品了,這其中最爲著名的一個就是 initng , 它已經可以用於 Debian 了,並且在 Ubuntu 上也能工作。在同一位置上,Solaris 使用 SMF (Service Management Facility),而 Mac OS 則使用 launchd。而 Ubuntu 則更傾向於集這些軟件的優點於一身。
Sysvinit daemon 是一個基於運行級別的初始化程序,它使用了運行級別(如單用戶、多用戶等)並通過從 /etc/rc?.d 目錄到 /etc/init.d 目錄的初始化腳本的鏈接來啓動與終止系統服務。自從 Feisty 開始,Ubuntu 轉向了 Upstart init daemon,並開始將Sysvinit 設置轉換爲 Upstart 的設置。本文探討 Upstart 和殘存的部分 Sysvinit 遺蹟:/etc/rc?.d 和 /etc/init.d 目錄以及運行級別的概念。
Upstart init daemon 是基於事件的,當系統中的什麼情況發生變化時,它會運行某個特定的程序。這裏被運行的程序多半是用來啓動或終止服務的腳本。這個配置方式和systemv 在系統進入某個運行級別的時候運行init腳本的鏈接的概念實際上是非常類似的,只不過 upstart 更加靈活一些。Upstart 不僅能在運行級別改變的時候啓動或終止服務,也能在接收到系統發生其他改變的信息的時候啓動或終止服務。這些系統的改變被稱爲“事件”。例如,當 upstart 從 udev 接收到運行時文件系統加載、打印機安裝或其他類似的設備添加或刪除的信息,並採取相應的行動。Upstart 也可以在系統啓動、關閉或某個任務狀態改變的時候啓動或關閉服務。
Upstart由五個包組成,(Ubuntu 中)它們都會被缺省安裝:

  • upstart 提供Upstart init daemon 和 initctl 工具。
  • upstart-logd 提供 logd daemon 和 logd 服務的工作定義文件。
  • upstart-compat-sysv 提供了 rc 任務的工作定義文件,並提供了 reboot, runlevel, shutdown, telinit 等工具,以便與 sysvinit 相兼容。
  • startup-tasks 提供了系統啓動任務的工作定義文件。
  • system-services 提供了 tty 服務的工作定義文件。

定義

有幾個名詞幫助我們理解 init 相關的東西。事件(event)是 init 可以得到的狀態變更信息。幾乎系統所有的內部或外部狀態變更都可以觸發一個事件。比如,引導程序會觸發啓動(startup)事件,系統進入運行級別2會 觸發運行級別2(runlevel 2)事件,而文件系統加載則會觸發路徑加載(path-mounted)事件,拔掉或安裝一個熱插拔或USB設備(如打印機)也會觸發一個時間。用戶還可 以通過 initctl emit 命令來手動觸發一個事件。
一個工作(job)是 init 可以理解的一系列指令。典型的指令包括一個程序(二進制文件或是腳本)和事件的名稱。Upstart init daemon 會在事件觸發的時候運行相應的程序。用戶可以分別用 initctl start 和 stop 命令手動啓動或終止一項工作。工作又可以分爲任務和服務。
任務是運行、並在執行結束後返回到等待狀態的工作。
服務是那些通常不會自己結束的工作。比如,logd daemon 和 gettys 就被實現爲服務。init daemon 會監測每個服務的狀態,如果服務出現問題會重啓服務,在某些事件觸發時或手工停止時會殺死服務。
/etc/event.d 目錄下包含着一系列的工作定義文件(定義了 upstart init daemon 運行的工作的文件)。最初,這個目錄由 Upstart 包來生成。在 Feisty 之後的 Ubuntu 中,被安裝的服務會向這個目錄中添加控制服務的文件,替代哪些安裝到 /etc/rc?.d 和 /etc/init.d 目錄的文件。
Upstart init daemon 的核心是一個狀態機。它持續跟蹤各個工作的狀態,當有事件觸發的時候,跟蹤工作的狀態改變。當 init 跟蹤到一個工作的狀態從一個轉變到了另一個的時候,就可能會執行工作的命令或是終止工作。
System V 的 init daemon 通過改變運行級別來啓動或停止服務。而使用 Upstart init daemon 的 Ubuntu 系統沒有運行級別的概念。爲了將基於運行級別的系統平滑移植到基於事件的系統,併爲面向其他發佈版的軟件提供一定的兼容性,Ubuntu 使用 Upstart 模擬了運行級別。
在 /etc/event.d/rc? 文件中定義的 rc? 工作會運行 /etc/init.d/rc 腳本,這個腳本會運行鏈接到 /etc/rc?.d 目錄中的 /etc/init.d 中的啓動腳本,以模擬 SysVinit 的行爲。當系統進入一個運行級別的時候,rc? 工作就會運行這些腳本。同時,Upstart 提供了 runlevel 和 telinit 工具以提供與 SysVinit 的兼容性。
使用 initctl (init control) 工具,具有 root 權限的管理員可以和 Upstart init daemon 通信。這個工具可以用來啓動、停止或報告(report)一項工作。 比如,initctl list 命令會列出所有的工作和它們的狀態:

$ sudo initctl list 

logd (stop) waiting
rc-default (stop) waiting
rc0 (stop) waiting

tty5 (start) running, process 4720
tty6 (start) running, process 4727

要獲得更詳細的信息,可以參考 initctl 的 man page 或本節的例子。使用 initctl help 命令 (help 前沒有橫槓)可以列出 initctl 的命令列表。此外,也可以用 initctl list –help 來列出 list 命令的幫助信息,當然,將 list 換乘其它的 initctl 命令會得到該命令對應的信息。start, stop 和 status 工具是 initctl 的鏈接,會直接運行 initctl 的對應命令。

工作(Job)

/etc/event.d 目錄下的每個文件都定義了一個工作,其中至少應該包含一個事件和一個命令。當事件被觸發的時候,init 執行對應的命令。本節將介紹管理員自定義的工作和 Upstart 包中包含的工作。

下面的管理員自定義的工作使用 exec 關鍵字執行了一條 shell 命令。實際上,也可以用這個關鍵字執行一個 shell 腳本或一個二進制可執行文件。

$ cat /etc/event.d/mudat 

start on runlevel 2
exec echo “Entering multiuser mode on ” $(date) > /tmp/mudat.out

這個文件定義了一個任務:當系統進入到多用戶模式(運行級2)的時候執行 echo 命令。這個命令會向 /tmp/mudat.out 文件寫出一條包含日期時間消息。shell 會運行 date 命令替換其中的內容。在任務結束後, mudat 任務會停止並進入等待狀態。
在下一個的例子中,cat 命令展示了 /tmp/mudat.out 文件的內容和 initctl list 命令關於這個任務的輸出(status 工具也可以得到同樣的信息):

$ cat /tmp/mudat.out 

Entering multiuser mode on Tue Jul 10 17:34:39 PDT 2007

$ sudo initctl list mudat
mudat (stop) waiting

如果 exec 命令行中包含 shell 的特殊字符, init 會運行 /bin/sh(dash 的符號鏈接)並把命令行交給它來處理。否則,exec 會直接運行命令行。如果要執行多個 shell 命令,可以把他們放到腳本文件中並運行腳本,或是使用 script….end script (下面會介紹)。
Upstart initdaemon  只能監測哪些使用 exec 運行的工作(服務),無法監測使用 script…end script 運行的工作。換句話說,服務應該使用 exec 運行,而任務則可以使用任意的方法。

myjob 示例

用戶也可以自己定義一個事件,並讓一個工作被這個事件觸發。如下的 myjob 工作定義文件定義了一個被 hithere 事件觸發的工作:

$ cat /etc/event.d/myjob 

start on hithere
script
echo “Hi there, here I am!” > /tmp/myjob.out
date >> /tmp/myjob.out
end script

myjob 文件提供了另一種運行命令的方法:在 script 和 end script 關鍵字之間包含了兩行命令。這兩個關鍵字常常導致 init 去運行 /bin/sh。例中的命令將一條消息和日期輸出到了 /tmp/myjob.out 文件。現在可以使用 initctl emit 命令觸發這個工作。如下,init 展示了 myjobs 在我們的觸發下所經歷的各個狀態:

$ sudo initctl emit hithere

hithere
myjob (start) waiting
myjob (start) starting
myjob (start) pre-start
myjob (start) spawned, process 6064
myjob (start) post-start, (main) process 6064
myjob (start) running, process 6064
myjob (stop) running
myjob (stop) stopping
myjob (stop) killed
myjob (stop) post-stop
myjob (stop) waiting

$ cat /tmp/myjob.out
Hi there, here I am!
Sat Jul 7 20:19:13 PDT 2007

$ sudo initctl list myjob
myjob (stop) waiting

在上面的例子裏,cat 展示了 myjob 產生的輸出,initctl 展示了工作的狀態。同樣也可以用 initctl start myjob(或直接用 start myjob)來運行它。initctl start 十個非常有用的命令,這樣你就可以在沒有事件的情況下啓動一個工作。比如,你可以用 initctl start mudat 來直接運行前面例子中的 mudat 工作而不會觸發 runlevel 2 事件。

指定帶參數的事件

telinit 和 shutdown 工具發送帶有參數的 runlevel 事件。比如,shutdown 發送 runlevel 0,telinit 2 會發送 runlevel 2 事件。你可以在工作定義中用如下格式匹配這些事件:
start | stop on event [arg ]

其中 event 是一個事件,而 arg 是一個可選參數。要在系統進入 runlevel 2 的時候停止一個工作,可以指定 stop on runlevle 2,也可以指定 runlevel [235] 來匹配運行級 2, 3 和 5,或用 runlevel [!2] 來匹配 2 之外的運行級。
儘管 Upstart 會忽略掉多餘的事件參數,但工作定義文件中的事件名稱裏的參數必須在事件中存在。比如,沒有參數的 runlevel 可以匹配所有的 runlevel 事件,不論是否有參數,但 runlevel S arg2 將不會匹配任何事件,因爲 runlevel 事件只會帶有一個參數。

/etc/event.d 中的工作定義文件

隨着 Ubuntu 從 SysVinit 向 Upstart 的遷移,更多地工作會在 /etc/event.d 文件中定義。本節介紹一些 Upstart 包放在這個目錄中的工作定義文件。

/etc/event.d/rc2 工作定義文件定義了 rc2 任務,這和其他的 rc? 任務沒什麼區別。rc2 任務在系統進入到多用戶模式的時候會被觸發(事件名稱是 runlevel 2);當系統進入到其它任意運行級的時候(runlevel [!2])會結束。腳本的第一個部分調用 runlevel 工具,它會讓系統顯示自己在運行級2 (當然,實際上已經沒有運行級這個玩意兒了)並給兩個變量賦值。接下來的工作由 exec 命令完成,它會使用參數 2 運行 /etc/init.d/rc 腳本。這個腳本使用相應的參數調用 /etc/rc?.d 目錄中的那些鏈接。這裏 rc2 任務會運行 /etc/rc2.d 下的符號鏈接對應的 init 腳本。

$ cat /etc/event.d/rc2 

# rc2 - runlevel 2 compatibility
#
# This task runs the old sysv-rc runlevel 2 (”multi-user”) scripts. It
# is usually started by the telinit compatibility wrapper.

start on runlevel 2

stop on runlevel [!2]

console output
script
set $(runlevel –set 2 || true)
if [ “$1″ != “unknown” ]; then
PREVLEVEL=$1
RUNLEVEL=$2
export PREVLEVEL RUNLEVEL
fi

exec /etc/init.d/rc 2
end script

tty 服務

如下是一個在 tty1 上啓動並監視 getty 進程的服務的工作定義文件:

$ cat /etc/event.d/tty1 

# tty1 – getty
#
# This service maintains a getty on tty1 from the point when
# the system is started until it is shut down again.

start on runlevel 2
start on runlevel 3
start on runlevel 4
start on runlevel 5

stop on runlevel 0
stop on runlevel 1
stop on runlevel 6

respawn
exec /sbin/getty 38400 tty1

這個服務由 runlevel 2 到 5 (多用戶模式)來觸發,啓動 getty 進程,並在系統關閉、重啓或進入單用戶模式,即運行級 0,1 和 6 時觸發來關閉該服務。respawn關鍵字告訴 init 在服務終止後重啓服務,而 exec 命令是讓 getty 進程以 38400 波特率運行在 tty1。如下,initctl 工具顯示該服務處於啓動狀態,進程ID 4747,ps 命令顯示該服務的進程:

$ sudo initctl list tty1 

tty1 (start) running, process 4747

$ ps -ef | grep 4747
root 4747 1 0 Jul02 tty1 00:00:00 /sbin/getty 38400 tty1

rc-default 任務和 inittab

在 SysVinit 中,/etc/inittab 文件通過 initdefault 項告訴 init 在系統啓動的時候進入哪個運行級,而 Ubuntu 沒有 inittab 文件,缺省的,Upstart init daemon (使用 rc-default 任務)引導系統進入多用戶模式(缺省運行級爲2)。如果希望系統啓動進入其他運行級別,那麼就創建一個 inittab 文件。如下會讓系統缺省進入單用戶模式 (runlevel S):

$ cat /etc/inittab 

:id:S:initdefault:

當系統進入到單用戶(修復)模式,如果系統的root帳號沒有被鎖定,init 會在顯示 root 提示符之前要求輸入 root 密碼。否則,它會不要求輸入密碼而直接顯示 root 提示符。

注意:不要將系統設置啓動到運行級別 0 或 6,這樣系統將永遠無法正常啓動。要直接進入多用戶模式(運行級 2),如果有 inittab 刪除這個文件,或者用上述的例子,將裏面的 S 替換成 2.

Upstart的未來

從 SysVinit 到 Upstart 的遷移涉及到了 Linux 系統的很多部分。要讓這個轉換儘量平滑並且引入儘量少的問題,Upstart 團隊決定通過多個 release 來完成這個遷移。

Ubuntu 從 Feisty 開始使用 Upstart init daemon。在 Feisty 和 Gutsy+2 之間,Ubuntu 將完成 SysVinit 到更加乾淨、更加靈活的 Upstart 的遷移。隨着越來越多的服務被放到 Upstart 的控制之下,/etc/event.d 目錄下的內容將會替代 /etc/init.d 和 /etc/rc?.d 目錄下的內容。運行級將不再作爲 Ubuntu 正式支持的特徵,雖然它們可能爲了保持與第三方軟件的兼容性而繼續保留。最終 Upstart 將會替換掉 crond。

 

*********************************************************************

轉 到kubuntu之前曾經學習了一下,瞭解到ubuntu在6.10開始用upstart替代init,主要腳本都在/etc/event.d下面,默認 情況下/etc下沒有inittab文件。

剛裝上kubuntu時候專門到/etc/event.d下看了一下,特別注意到rc- default這個腳本,裏面有一段內容:
說明默認情況下inittab雖然不存在,但是用戶建立的inittab還是會被注意到的。

然 後又經別人的指點看了一下/usr/share/doc/upstart/下面的文檔,其中README.Debian中有這麼一段內容:

這 就給我這樣一個印象,即雖然ubuntu用upstart替代init,但還是和init保持兼容。

今天正好需要將系統直接啓動到字符界 面下,即不啓動kdm。

那 就試試自建一個inittab文件,並按照以前的習慣寫入一行id:3:initdefault: ,保存後重新啓動,結果發現毫無變化,依然啓動到桌面,有點納悶,難道inittab不起作用?在終端裏輸入runlevel檢查當前狀態,顯示 N 3,說明inittab有效果,那是什麼原因呢?

將剛纔建立的inittab移除,將系統恢復到之前的狀態並重新啓動,再用 runlevel檢查,顯示 N 2,說明ubuntu系統的default runlevel可能是2,這和我以前的常識有些衝突,看來又需要學習了。

先 去分別查看/etc/rc2.d至rc5.d下的內容,發現基本一致,都啓動了kdm。這與其他的linux發行版不太一致,通常runlevel 3是Multi user mode,即直接登錄到字符界面;而runlevel 5是Multi user mode with GUI,即登錄到圖形界面。

後來在Debian的FAQ裏面搜索到這樣的內容:

小區別就在這裏了,看來debian以及 衍生出來的發行版,如ubuntu的default runlevel確實是2,而且id 2至5都是一樣的。

真相大白,再次建立 inittab,寫入id:3:initdefault: ,然後進入/etc/rc3.d,將S13kdm移動到其他目錄備份起來,重新啓動系統,如願以償進入字符界面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章