在前面的文章裡,我們學習了怎麼顯示和管理運行中的進程。今天,我想描述一下那些進程實際上如何啟動的。
當你啟動計算機進入FreeBSD時會有很多巧妙的事情發生。在此我無法論及所有的細節,但FreeBSD handbook極好地解釋了引導過程。當你啟動你的計算機時,可能會注意到內核對硬件作了檢測並在終端上顯示了相應的結果。當檢測結束時,內核會啟動兩個進程:進程 0 (swapper)和進程 1 (init)。
負責進程控制初始化的程序是init;沒有它,其它的進程無法啟動。在引導時,init要做兩項重要的工作:首先,它在rc的控制之下裝入啟動腳本,然後它初始化終端以便使用戶可以登錄。讓我們分別描述這些功能,從rc開始:
whatis rc
rc(8) - command scripts for auto-reboot and daemon startup
這些腳本實際位於/etc/rc;通常,位於/etc下的這個配置文件對應於手冊的第五部分,所以你可以根據手冊對配置文件作正確地修改。但是,如果你打:
man 5 rc
你卻會得到以下信息:
No entry for rc in section 5 of the manual
這對於上面提到的它位於手冊第八部分來說看起來有點古怪,因為這部分手冊包含的是系統維護和操作命令,通常它們都是後台進程。讓我們進一步來看一下這個文件:
more /etc/rc
# System startup script run by init on autoboot
# or after single-user.
# Output and error are redirected to console by init,
# and the console is the controlling terminal.
# Note that almost all of the user-configurable behavior
# Please check that file first before contemplating any changes
# here. If you do need to change this file for some reason, we
# would like to know about it.
好,相當清晰;看來對於該文件我們自己是不能亂來的。這裡有些相當重要的東西對於正確引導我們的系統所必須的。讓我們往後跳,看一些重要的部分來找出啟動時實際上都發生了些什麼。注意當在引導過程中處理rc腳本時,init會把所有的輸出和錯誤信息記錄到終端上。
rc首先做的事之一就是設置路徑變量,以使它可以找到你FreeBSD系統上的可執行程序:
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin
# If there is a global system configuration file, suck it in.
fi
接著它會作一個文件系統連貫性檢查。如果你曾經非正常關閉FreeBSD系統,你會在引導過程中看到它在這一步上發出抱怨的。
echo Automatic boot in progress...
fsck -p
假定fsck沒有遇到任何問題,它就馬上裝載你的文件系統:
# Mount everything except nfs filesystems.
mount -a -t nonfs
在其它任何進程啟動之前,你的CMOS時鐘必須調整為內核時鐘可以理解的形式:
adjkerntz -i
然後整理var目錄,將引導信息寫入dmesg.boot:
clean_var() {
if [ ! -f /var/run/clean_var ]; then
rm -rf /var/run/*
find /var/spool/lock ! -type d -delete
rm -rf /var/spool/uucp/.Temp/*
# Keep a copy of the boot messages around
dmesg >/var/run/dmesg.boot
接著rc會讀取以下文件:
/etc/rc.sysctl
/etc/rc.pccard
然後復位終端權限:
# Whack the pty perms back into shape.
chflags 0 /dev/tty[pqrsPQRS]*
chmod 666 /dev/tty[pqrsPQRS]*
chown root:wheel /dev/tty[pqrsPQRS]*
並清理自己產生的“垃圾”以及/tmp目錄:
# Clean up left-over files
# Clearing /tmp at boot-time seems to have a long tradition. It doesn't
# help in any way for long-living systems, and it might accidentally
# clobber files you would rather like to have preserved after a crash
# (if not using mfs /tmp anyway).
# See also the example of another cleanup policy in /etc/periodic/daily.
# Remove X lock files, since they will prevent you from restarting X11
# after a system crash.
現在rc准備啟動一些後台進程,首先是syslogd和named:
# Start system logging and name service. Named needs to start before syslogd
然後是inetd、cron、lpd、sendmail、sshd和usbd:
# Now start up miscellaneous daemons that don't belong anywhere else
接著rc將更新motd(每日信息)並執行“uname -m”,這條命令會在屏幕上顯示構架類型。
(/etc/rc文件結尾)
當到達/etc/rc的結尾時,rc的工作就完成了。在此我們重新回顧一下:init調用rc腳本,它會讀取一些全局的和本地的配置文件以正確裝載文件系統並建立系統後台進程可以啟動的環境。你的操作系統現在已經啟動並運行著,但到此為止,還沒有一個用戶可以與操作系統實際交互的環境。這就是init第二個重要的功能。
配置文件/etc/ttys將會被讀取以決定初始化的終端。不象/etc/rc,該文件可以由超級用戶經常編輯以確保讓init來初始化所需的終端。
為了理解此文件,我們必須了解在你的FreeBSD系統上有三種類型的終端。以“ttyv”開頭後跟一個數字的是虛擬終端;它們都是用戶可以獲得的物理存在於FreeBSD系統上的終端。缺省情況下,這些虛擬終端中的第一個,或叫“ttyv0”,表示控制台。以“ttyd”開頭後跟一個數字的是串行線路或撥號終端;它們是用戶用調制解調器遠程訪問你的FreeBSD系統時可獲得的終端。最後一種終端類型就是偽終端或網絡終端;它們以“ttyp”開頭後跟一個數字或字母,用於通過網絡連接訪問你的FreeBSD系統。
如果我們用以下命令看此文件:
more /etc/ttys
我們會看到此文件分為三部分,每個部分分別對應這三種類型的終端。同時每個部分還分為四欄,總結為下面的圖表:
欄名
含義
name
終端設備的名稱
getty
在終端上啟動運行的程序,通常為getty。其它項目包括xdm,它用來啟動X Window系統,或none,說明沒有程序。
type
對於虛擬終端,對應的類型為cons25。其它通常的值包括偽終端network,調制解調器入口dialup,以及unknown,用於用戶試圖以無法預定的類型進行連接的終端。
status
它必須是on或off。如果是on,init將運行getty欄指定的程序。如果出現單詞“secure”,說明該tty允許root登錄;為避免這種情況,可以用單詞“insecure”。
讓我們從虛擬終端部分開始解釋該文件;可以看到它是以設置控制台開始的:
# If console is marked "insecure", then init will ask
# for the root password when going to single-user mode.
console none unknown off secure
如果在引導過程中fsck命令運行出了問題,init將使你的FreeBSD系統進入單用戶模式以使root用戶可以修復問題。如果你用insecure代替secure來設置控制台,init將在你可以繼續干之前索取口令。
ttyv0 "/usr/libexec/getty Pc" cons25 on secure
# Virtual terminals
ttyv1 "/usr/libexec/getty Pc" cons25 on secure
ttyv2 "/usr/libexec/getty Pc" cons25 on secure
ttyv3 "/usr/libexec/getty Pc" cons25 on secure
ttyv4 "/usr/libexec/getty Pc" cons25 on secure
ttyv5 "/usr/libexec/getty Pc" cons25 on secure
ttyv6 "/usr/libexec/getty Pc" cons25 on secure
ttyv7 "/usr/libexec/getty Pc" cons25 on secure
ttyv8 "/usr/X11R6/bin/xdm -nodaemon" xterm off secure
你可以看到在我的FreeBSD系統上除了控制台以外還有八個虛擬終端;我可以通過按ALT鍵加一個控制鍵來訪問每個終端。例如ALT F1可訪問控制台,ALT F2訪問ttyv1, ALT F3訪問ttyv2,等等。如果我啟動了X會話,那麼它可以用ALT F8來訪問。如果我把ttyv8上的單詞off改為on,那麼我可以在引導時得到一個X終端而不是控制台。然後可以繼續用我ALT加功能鍵來訪問其它終端。我的所有這些虛擬終端都標為“secure”,說明它們都可接受root登錄。你可以有多少個虛擬終端是由你的FreeBSD版本所決定的;如果你希望建立更多虛擬終端,可以讀一下這個faq。
現在讓我們移到撥號終端:
# Serial terminals
# The 'dialup' keyword identifies dialin lines to login, fingerd etc.
ttyd0 "/usr/libexec/getty std.9600" dialup off secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
你可以看到我有四個可獲得的撥號終端,但它們都被關閉了。如果我想讓用戶通過調制解調器訪問我的FreeBSD系統,我就必須至少打開它們中的一個,還必須決定是否讓這些用戶可以用root身份登錄;如果不,就把單詞“secure”改為“insecure”。你是否看到getty欄含有數字9600,它說明數據傳輸率為9600bps。因為現在很多調制解調器都有更高的速率,我也可以把它改為57600。最後,最好讀一下FreeBSD handbook中的撥號服務部分。
/etc/ttys文件的最後一部分是網絡或偽終端。你會看到它們有很多,准確地說是255個,范圍從:
# Pseudo terminals
ttyp0 none network
到
ttySv none network
且缺省情況下它們都沒有被允許。
如果你為了使/etc/ttys更改生效以使init使用這些更改,可以超級用戶的身份向init發送一個HUP信號,如:
kill -1 1
這裡前面的-1代表信號1(HUP),而後面1代表進程1(init)。
那麼現在,哪個/etc/ttys文件中提到的getty程序繼續保持運行呢?man 8中對getty的描述如下:
描述
init(8)調用getty程序打開並初始化tty行,讀取一個登錄名,然後調用login(1)。
所以,init讀取/etc/ttys並在每個你在配置文件中設定的終端上啟動一個getty進程。getty的工作是監視終端看是否有人試圖登錄。如果有的話,getty將啟動登錄程序校驗用戶的登錄名和口令。如果校驗合格,登錄程序會啟動用戶的登錄命令解釋器並把用戶置於他們的宿主目錄下。當用戶具有一個命令解釋器後,他們就可以與操作系統交互了。現在它就可以讓命令解釋器解釋用戶的輸入並確保啟動了必要的進程。
當一個用戶退出登錄時,再次調用init啟動其它的getty進程以繼續監視終端等待其它的登錄嘗試。
讓我們看一下ps命令的輸出來總結一下剛才這次引導的整個過程,FreeBSD 4.1缺省已經安裝了ps程序。我將用-ax開關以包含系統進程:
ps -ax
PID TT STAT TIME COMMAND
0 ?? DLs 0:00.01 (swapper)
1 ?? ILs 0:00.16 /sbin/init --
2 ?? DL 0:00.02 (pagedaemon)
3 ?? DL 0:00.00 (vmdaemon)
4 ?? DL 0:00.02 (bufdaemon)
5 ?? DL 0:01.02 (syncer)
1056 ?? Is 0:00.00 adjkerntz -i
1187 ?? Ss 0:00.08 syslogd -s
1206 ?? Is 0:00.05 inetd -wW
1208 ?? Is 0:00.11 cron
1622 ?? Ss 0:00.02 sendmail: accepting connections on port 25 (sendmail)
1621 v0 Ss 0:00.12 -csh (csh)
1701 v0 R+ 0:00.00 ps -ax
1699 v1 Is+ 0:00.01 /usr/libexec/getty Pc ttyv1
1619 v2 Is+ 0:00.01 /usr/libexec/getty Pc ttyv2
1618 v3 Is+ 0:00.01 /usr/libexec/getty Pc ttyv3
1617 v4 Is+ 0:00.01 /usr/libexec/getty Pc ttyv4
1616 v5 Is+ 0:00.01 /usr/libexec/getty Pc ttyv5
1615 v6 Is+ 0:00.01 /usr/libexec/getty Pc ttyv6
1614 v7 Is+ 0:00.01 /usr/libexec/getty Pc ttyv7
現在你可以認識很多進程:swapper的PID為0而init的為1。adjkerntz,syslogd,inetd,cron和sendmail都是由rc啟動的。當然,我必須在一個命令解釋器上運行這個ps命令;在上述情況中,它是從ttyv0上的c shell解釋器運行的。getty進程等待虛擬終端1-7上的登錄。而在虛擬終端8上則沒有運行getty進程,因為該終端在/etc/ttys中被標為了“off”。