pstree命令显示我机器上运行的进程的树状图,看起来有点像目录结构。最引人注目(至少在我看来)的是,树状图的“根”是“init”。从init派生出所有其他进程。这并非偶然。
啊哈,令人吃惊!它说:“用于与sysv兼容的init进程”。这是Slackware吗?没错,Slackware确实是用Sys V的init。纯粹为了开心,我们看看Slackware的软件包说明:
玩够了......让我们看看 /etc/inittab 的格式。同样来自manpage:
<pre>
id:runlevels:action:process
Lines beginning with `#' are ignored.
id is a unique sequence of 1-4 characters which iden-
tifies an entry in inittab (for versions of
sysvinit compiled with libraries < 5.2.18 or a.out
libraries the limit is 2 characters).
Note: For gettys or other login processes, the id
field should be the tty suffix of the corresponding
tty, e.g. 1 for tty1. Otherwise, the login
accounting might not work correctly.
runlevels
lists the runlevels for which the specified action
should be taken.
action describes which action should be taken.
process
specifies the process to be executed. If the pro-
cess field starts with a `+' character, init will
not do utmp and wtmp accounting for that process.
This is needed for gettys that insist on doing
their own utmp/wtmp housekeeping. This is also a
historic bug.
</pre>
组成这个文件的每行都有4个部分,用“:”分隔开
- id - 该行的标识
- runlevels - 该行为应该发生的运行级的列表
- action - 应发生的行为,你能在inittab的manpage里面发现对它们的概述。我将在适当的时候引用。
- process - 应由init启动的进程。
复杂吗?还不是那么坏吧,这是我的 /etc/inittab:
<pre>
bilbo@bilbo:~$ cat /etc/inittab
#
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Version: @(#)inittab 2.04 17/05/93 MvS
# 2.10 02/10/95 PV
# 3.00 02/06/1999 PV
# 4.00 04/10/2002 PV
#
# Author: Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
# Modified by: Patrick J. Volkerding, <volkerdi@slackware.com>
#
# These are the default runlevels in Slackware:
# 0 = halt
# 1 = single user mode
# 2 = unused (but configured the same as runlevel 3)
# 3 = multiuser mode (default Slackware runlevel)
# 4 = X11 with KDM/GDM/XDM (session managers)
# 5 = unused (but configured the same as runlevel 3)
# 6 = reboot
# Default runlevel. (Do not set to 0 or 6)
id:3:initdefault:
# System initialization (runs when system boots).
si:S:sysinit:/etc/rc.d/rc.S
# Script to run when going single user (runlevel 1).
su:1S:wait:/etc/rc.d/rc.K
# Script to run when going multi user.
rc:2345:wait:/etc/rc.d/rc.M
# What to do at the "Three Finger Salute".
ca::ctrlaltdel:/sbin/shutdown -t5 -r now
# Runlevel 0 halts the system.
l0:0:wait:/etc/rc.d/rc.0
# Runlevel 6 reboots the system.
l6:6:wait:/etc/rc.d/rc.6
# What to do when power fails.
pf::powerfail:/sbin/genpowerfail start
# If power is back, cancel the running shutdown.
pg::powerokwait:/sbin/genpowerfail stop
# These are the standard console login getties in multiuser mode:
c1:1235:respawn:/sbin/agetty 38400 tty1 linux
c2:1235:respawn:/sbin/agetty 38400 tty2 linux
c3:1235:respawn:/sbin/agetty 38400 tty3 linux
c4:1235:respawn:/sbin/agetty 38400 tty4 linux
c5:1235:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux
# Local serial lines:
#s1:12345:respawn:/sbin/agetty -L ttyS0 9600 vt100
#s2:12345:respawn:/sbin/agetty -L ttyS1 9600 vt100
# Dialup lines:
#d1:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS0 vt100
#d2:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS1 vt100
# Runlevel 4 used to be for an X window only system, until we discovered
# that it throws init into a loop that keeps your load avg at least 1 all
# the time. Thus, there is now one getty opened on tty6. Hopefully no one
# will notice. ;^)
# It might not be bad to have one text console anyway, in case something
# happens to X.
x1:4:wait:/etc/rc.d/rc.4
# End of /etc/inittab
bilbo@bilbo:~$
</pre>
只有74行文字(仅约一张A4纸的篇幅),如果你把那些注释去掉,将只剩下16行。但你将学到那些注释很值得读一读。
让我们开始把/etc/inittab拆开:
<pre>
# Author: Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
# Modified by: Patrick J. Volkerding, <volkerdi@slackware.com>
</pre>
这个文件的最后版本是由Miquel van Smoorenburg(听起来像德国人)制作的,然后由Patrick J. Volkerding(Slackware的维护者,对,Slackware是一个人的工作)为了用于Slackware而作了修改。这个注释没有太大作用,但它确实在那里。我们把它当作GPL的实例好了。
<pre>
# These are the default runlevels in Slackware:
# 0 = halt
# 1 = single user mode
# 2 = unused (but configured the same as runlevel 3)
# 3 = multiuser mode (default Slackware runlevel)
# 4 = X11 with KDM/GDM/XDM (session managers)
# 5 = unused (but configured the same as runlevel 3)
# 6 = reboot
</pre>
又是一些注释,当然它什么事都不做,但它让接下来的事情清晰了很多。你看到的是对Slackware用到的7个运行级的描述。运行级2和5都没有用到,往后你会看到它们和运行级3用同样的脚本。
<pre>
# Default runlevel. (Do not set to 0 or 6)
id:3:initdefault:
</pre>
终于要做些事情了。它是一个正常的inittab行,4个部分用“:”隔开。在这里的行为是“initdefault”,按照manpage的说法:
<pre>
initdefault
An initdefault entry specifies the runlevel which should
be entered after system boot.
If none exists, init will ask for a runlevel on the console.
The process field is ignored.
</pre>
在这里定义了缺省的运行级,当然注释就是这么说的。因此,Slackware缺省的运行级是3,用控制台/文本登录的多用户运行级。
假如系统启动时,Slackware没有接到进入其他运行级的信号(例如"telinit 1"这类命令),它就会进入这个运行级。
<pre>
# System initialization (runs when system boots).
si:S:sysinit:/etc/rc.d/rc.S
</pre>
这里的行为是“sysinit”(系统初始化),意思很明显,但我们还是看看manpage:
<pre>
sysinit
The process will be executed during system boot.
It will be executed before any boot or bootwait
entries. The runlevels field is ignored.
</pre>
虽然这里指明了运行级S,但实际上被忽略了。将执行的程序是/etc/rc.d/rc.S,是个脚本。你会看到Slackware所有的启动脚本都在/etc/rc.d。在/etc/rc.d/rc.S里,系统将被初始化,我们接下来讨论这个脚本时就会看到。
<pre>
# Script to run when going single user (runlevel 1).
su:1S:wait:/etc/rc.d/rc.K
</pre>
这里的行为是wait:
<pre>
wait The process will be started once when the specified
runlevel is entered and init will wait for its
termination.
</pre>
其中定义的脚本将在进入运行级1和S(意思是single)时被调用。“wait”行为将使init保留所有其他的动作直至/etc/rc.d/rc.K执行完毕。/etc/rc.d/rc.K启动了单用户运行级所需的服务,迟一点你会看到那不算很多。
<pre>
# Script to run when going multi user.
rc:2345:wait:/etc/rc.d/rc.M
</pre>
在这里的行为还是wait。这里定义了运行级2、3、4、5都调用/etc/rc.d/rc.M,基本上是除了运行级1之外的所有“常规”的运行级。一会儿我们会看到那是个怎样的脚本。
目前为止我们所看到的行是用来定义系统启动之后所发生的事。在所有情况下,init都会等到它启动的进程(脚本)终止。下面的几行是init在特殊情况下应做的事,而且不属于任何特定的运行级。
<pre>
# What to do at the "Three Finger Salute".
ca::ctrlaltdel:/sbin/shutdown -t5 -r now
</pre>
不单微软知道所谓的“三指礼”(译注:ctrl-alt-del),在Linux中你也能决定它出现时该做什么。下面是init的manpage中关于这个行为的条目:
<pre>
ctrlaltdel
The process will be executed when init receives the SIGINT
signal. This means that someone on the system console has
pressed the CTRL-ALT-DEL key combination. Typically one wants
to execute some sort of shutdown either to get into
single-user level or to reboot the machine.
</pre>
在Slackware的系统上,将会执行 /sbin/shutdown -t5 -r now 。
<pre>
SHUTDOWN(8) Linux System Administrator's Manual SHUTDOWN(8)
NAME
shutdown - bring the system down
SYNOPSIS
/sbin/shutdown [-t sec] [-arkhncfF] time [warning-message]
DESCRIPTION
shutdown brings the system down in a secure way. All
logged-in users are notified that the system is going
down, and login(1) is blocked. It is possible to shut the
system down immediately or after a specified delay. All
processes are first notified that the system is going down
by the signal SIGTERM. This gives programs like vi(1) the
time to save the file being edited, mail and news process-
ing programs a chance to exit cleanly, etc. shutdown does
its job by signalling the init process, asking it to
change the runlevel. Runlevel 0 is used to halt the sys-
tem, runlevel 6 is used to reboot the system, and runlevel
1 is used to put to system into a state where administra-
tive tasks can be performed; this is the default if nei-
ther the -h or -r flag is given to shutdown. To see which
actions are taken on halt or reboot see the appropriate
entries for these runlevels in the file /etc/inittab.
</pre>
参数 -r 的意思是重新启动(reboot),做法是给init进入运行级6的信号。延迟时间是从现在开始5秒。如果你有保持运行时间的癖好(uptime junkie),你可以让“三指礼”做完全不同的事情,并有效地迫使用户明确给出shutdown命令。(假如你有个NT类系统和Linux共存的服务器群,取消ctrl-alt-del的功能并不是一件坏事。)
当然第2部分(译注:指定运行级)就不必要了,init是对外界信号作出反应。
<pre>
# Runlevel 0 halts the system.
l0:0:wait:/etc/rc.d/rc.0
</pre>
这里的行为又是wait,因此init又会等待脚本执行完毕。进入运行级0后,init将执行 /etc/rc.d/rc.0 (最后是零)。这个脚本所做的是让所有启动了的进程安全地停止。不是所有的程序都喜欢你直接拔出插头的。作为最后的行动,它将调用poweroff来关闭系统或通知用户可以拔插头了。
<pre>
# Runlevel 6 reboots the system.
l6:6:wait:/etc/rc.d/rc.6
</pre>
这一行与上面那行非常像,事实上 /etc/rc.d/rc.0 是指向 /etc/rc.d/rc.6的符号链接,两个脚本其实是同一个。脚本的调用方式决定了最后一步会怎么做。在运行级6中,最后的命令将是reboot。
<pre>
# What to do when power fails.
pf::powerfail:/sbin/genpowerfail start
# If power is back, cancel the running shutdown.
pg::powerokwait:/sbin/genpowerfail stop
</pre>
这两行的命令要合在一起说,因为它们有很多相关性。它们的行为是:
<pre>
powerwait
The process will be executed when the power goes
down. Init is usually informed about this by a pro-
cess talking to a UPS connected to the computer.
Init will wait for the process to finish before
continuing.
powerfail
As for powerwait, except that init does not wait
for the process's completion.
powerokwait
This process will be executed as soon as init is
informormed that the power has been restored.
</pre>
这两行都是电力中断(或实际上是UPS电量耗尽)时该做的事情。
/sbin/genpowerfail start由关闭系统开始,/sbin/genpowerfail stop则试图在电力恢复时中断关机的行为。你可能注意到用start参数时并不会等脚本执行完毕,假如等了,就不可能中断关机的过程。 /sbin/genpowerfail是个使用shutdown命令的脚本,假如你有UPS你应该读一读这个脚本。
我们现在已经到了系统将要运行的关头。系统初始化的脚本已经执行了,缺省运行级的脚本也已经执行了- 单用户运行级是/etc/rc.d/rc.K,多用户运行级2、3、4、5执行的是/etc/rc.d/rc.M;所有需要的后台服务也在运行。在多用户运行级中,假如在/etc/rc.d/rc.M中启动了telnet和ssh服务,用户已经可以用这些方式登录系统了。假如你的系统是一个没有显示器和键盘的服务器,你让它这么待着就行了。
目前还不可能做到的是从控制台登录,有时能从控制台登录是会很方便的。init的下一个任务就是控制台登录。
<pre>
# These are the standard console login getties in multiuser mode:
c1:1235:respawn:/sbin/agetty 38400 tty1 linux
c2:1235:respawn:/sbin/agetty 38400 tty2 linux
c3:1235:respawn:/sbin/agetty 38400 tty3 linux
c4:1235:respawn:/sbin/agetty 38400 tty4 linux
c5:1235:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux
</pre>
马上我们有了个新的行为,在inittab的manpage里:
<pre>
respawn
The process will be restarted whenever it termi-
nates (e.g. getty).
</pre>
因此,已经启动的进程/sbin/agetty若被终止了,会重新运行。很明显,为运行级1、2、3和5启动了tty1至tty6的虚拟控制台。这些都是没有X的运行级。例外的是tty6上运行的agetty也将在运行级4(有X的运行级)启动。
agetty的manpage让我们了解到agetty的实际行为:
<pre>
AGETTY(8) AGETTY(8)
NAME
agetty - alternative Linux getty
SYNOPSIS
agetty [-ihLmnw] [-f issue_file] [-l login_program] [-I
init] [-t timeout] [-H login_host] port baud_rate,...
[term]
agetty [-ihLmnw] [-f issue_file] [-l login_program] [-I
init] [-t timeout] [-H login_host] baud_rate,... port
[term]
DESCRIPTION
agetty opens a tty port, prompts for a login name and
invokes the /bin/login command. It is normally invoked by
init(8).
</pre>
可见,agetty在一个tty的端口等待用户登录,然后将运行/bin/login。
<pre>
LOGIN(1) LOGIN(1)
NAME
login - begin session on the system
SYNOPSIS
login [-p] [username] [ENV=VAR ...]
login [-p] [-h host] [-f username]
login [-p] -r host
DESCRIPTION
login is used to establish a new session with the system.
It is normally invoked automatically by responding to the
login: prompt on the user's terminal. login may be spe-
cial to the shell and may not be invoked as a sub-process.
Typically, login is treated by the shell as exec login
which causes the user to exit from the current shell.
Attempting to execute login from any shell but the login
shell will produce an error message.
</pre>
login在系统上启动了一个新的会话。让我们回头看看pstree的输出:
<pre>
bilbo@bilbo:~$ pstree
init-+-4*[agetty]
|-atd
|-bash
|-bash---startx---xinit-+-X
| `-xinitrc-+-bbmail
| `-blackbox-+-mozilla-bin---mozilla-bin---4+
| `-rxvt---bash---pstree
</pre>
该死!不是应该有6个agetty吗?呃,对,曾经确实是有6个。但很显然,我从虚拟控制台登录了两次,有一次没有运行其他的程序,另一次运行了startx,变成了X会话。
当我logout的时候agetty会重新运行,还记得respawn吗?有趣的是,假如agetty启动得更早,会不会多个用户通过同一个虚拟控制台登录呢?
*nix总是提供了与系统联系的多种途径,我们已经知道了其中的2种:
- 通过网络,用telnet、ssh之类工具
- 通过直接连接到系统的键盘、显示器和鼠标
但是,*nix还有其他途径与系统联系:
<pre>
# Local serial lines:
#s1:12345:respawn:/sbin/agetty -L ttyS0 9600 vt100
#s2:12345:respawn:/sbin/agetty -L ttyS1 9600 vt100
# Dialup lines:
#d1:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS0 vt100
#d2:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS1 vt100
</pre>
上面两段为串行线路启动agetty,用于串口连接的终端或者用modem拨号进入。
你可以看到,它们缺省情况是被注释掉的。但一旦你需要使用古老的VT类终端或拨号进入系统,你就要用到这些代码。
在最后,要由X来给我们提供一个图形界面的运行级:
<pre>
# Runlevel 4 used to be for an X window only system, until we discovered
# that it throws init into a loop that keeps your load avg at least 1 all
# the time. Thus, there is now one getty opened on tty6. Hopefully no one
# will notice. ;^)
# It might not be bad to have one text console anyway, in case something
# happens to X.
x1:4:wait:/etc/rc.d/rc.4
</pre>
注释首先解释了为运行级4在tty6留下一个getty的原因。行为是wait,执行/etc/rc.d/rc.4。这个脚本将依次搜索kdm(kde的登录管理器)、gdm(gnome的登录管理器),最后是xdm(缺省的X登录管理器)。它会运行它所找到的第一个。这些登录管理器可以和agetty类比,它们会等着用户登录,当用户退出后自己重新运行。
<pre>
bilbo@bilbo:~$ man kdm
No manual entry for kdm
bilbo@bilbo:~$
</pre>
显然,目前为止没有kdm的manpage。
<pre>
bilbo@bilbo:~$ man gdm
No manual entry for gdm
bilbo@bilbo:~$
</pre>
目前为止,gnome的用户也不关心gdm的manpage。(译注:Slackware 9.1中已经有了)
<pre>
XDM(1) XDM(1)
NAME
xdm - X Display Manager with support for XDMCP, host
chooser
SYNOPSIS
xdm [ -config configuration_file ] [ -nodaemon ] [ -debug
debug_level ] [ -error error_log_file ] [ -resources
resource_file ] [ -server server_entry ] [ -session ses-
sion_program ]
DESCRIPTION
Xdm manages a collection of X displays, which may be on
the local host or remote servers. The design of xdm was
guided by the needs of X terminals as well as The Open
Group standard XDMCP, the X Display Manager Control Proto-
col. Xdm provides services similar to those provided by
init, getty and login on character terminals: prompting
for login name and password, authenticating the user, and
running a ``session.''
</pre>
最后我们终于找到了,这是xdm的manpage的片段。由于我总是在运行级3,我没法告诉你很多有关它的事。
我们已经看到,init - 所有进程之母 - 从内核那里接管了系统,然后init将处理/etc/inittab文件,根据inittab的输入,init将依次:
- 设置缺省的运行级
- 运行系统初始化脚本 /etc/rc.d/rc.S 并等待它结束
- 运行指定运行级的脚本并等待它结束
- 运行级1是/etc/rc.d/rc.K
- 运行级2、3、4、5是/etc/rc.d/rc.M
- 运行级0(关机)是/etc/rc.d/rc.0
- 运行级6(重新启动)是/etc/rc.d/rc.6
- 决定在特殊情况,例如ctrl-alt-del或停电时应采取的行动
- 为运行级1、2、3和5启动agetty(还有运行级4时启动6号终端,但有其特殊原因)
- 为串口连接启动终端,尽管这不是缺省的行为
- 为运行级4启动图形界面的登录管理器
结论
事实上这些就是init做的全部事情。剩下的“只不过是脚本”了。但是,仔细读它们也是很有趣的,否则你不会知道在哪里加载模块以及启动网络。