在内核引导结束并启动/sbin/init之后,系统就转入用户态的运行。在这之后创建的一切进程,都是在用户态进行。
这里先要讲清楚一个概念:就是init进程虽然是从内核开始的,即在前面所讲的init/main.c中的init()函数在启动后就已经是一个核心线程,但在转到执行/sbin/init之后,内核中的init()就变成了/sbin/init程序,状态也转变成了用户态,也就是说核心线程变成了一个普通的进程。这样一来,内核中的init函数实际上只是init进程的入口,它在执行execve("/sbin/init",argv_init,envp_init)时改变成为一个普通的用户进程。这就是exec系列函数替换进程映像的变身大法,只要你学过unix环境下的多进程编程,应该都能理解这一点。
除此之外,它们的代码来源也有差别,内核中的init()函数的源代码在/init/main.c中,是内核的一部分。而/sbin/init程序的源代码是sysvinit软件包的一部分,可以从FTP://ftp.cistron.nl/pub/people/miq...程序,如:halt, init, killall5, last, lastb (链接至 last), mesg, pidof (链接至 killall5), poweroff (链接至 halt), reboot (链接至 halt), runlevel, shutdown, sulogin, telinit (链接至 init), utmpdump 和 wall等等。对于启动过程而言,这里最重要的就是init程序。它启动之后,要完成很多任务:检查文件系统,启动各种后台服务进程,最后为每个终端和虚拟控制台启动一个getty进程供用户登录。由于所有其它用户进程都是由init派生的,因此它又是其它一切用户进程的父进程。
init进程启动后,就要按照/etc/inittab的内容进程系统设置。下面就是manfrake9.0的inittab内容:
#
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Author: Miquel van Smoorenburg,
# Modified for RHS Linux by Marc Ewing and Donnie Barnes
#
# Default runlevel. The runlevels used by Mandrake Linux are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
id:5:initdefault:
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
# Things to run in every runlevel.
ud:nce:/sbin/update
# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
# When our UPS tells us power has failed, assume we have a few minutes
# of power left. Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly.
pf:owerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"
# If power was restored before the shutdown kicked in, cancel it.
pr:12345owerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"
# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
从中可以看到,inittab的每一行由四个字段组成。
分别是:
id:runlevels:actionrocess
id:是每一行的标识符,长度一般为2个字符而且在整个inittab中必须唯一。对于getty或其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。
runlevels:指定运行级别,一般使用0-6以及S或s。运行级别是指init进程的整个系统的一个运行状态,它定义了系统所提供的服务,常见的运行级别如下:0,系统终止;1,单用户模式;3没有网络文件系统支持的多用户模式;4,保留;5,启动到Xwindow;6,重新启动,S和s意义相同,表示单用户模式且无需inittab文件,因此也不必在inittab中出现,实际上,进入单用户模式时,init直接在控制台(/dev/console)上运行/sbin/sulogin。实际上,7-9的运行级别也是可以使用的,只是传统的Unix系统没有定义这几个级别。级别可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行。。
action:指出的是init程序执行process命令的方式。initdefault是一个非凡的action值,用于标识缺省的运行级别。当init进程由核心启动后,它将读取inittab中的initdefault项,取得其中的运行级别,并作为当前的运行级别。假如没有inittab文件,或者其中没有initdefault项,init将在控制台上请求输入运行级别。respawn 在进入相应runlevel时执行,假如该进程结束,init会再起一个进程执行同样的命令。once在进入有runlevels指定的运行级别时运行并且只执行一次。wait的执行效果和once一样,但init会等待该命令结束。ctrlaltdel指定在用户按下Ctrl-Alt-Del时执行的命令。powerfail 在系统接收到掉电信号时启动相关进程。powerwait 和powerfail一样,在系统接收到掉电信号时启动相关进程,并等待进程终止,在进程终止前不做任何其它操作。off 若该项相关进程已经存在则强行终止,否则忽略。sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的运行级别,其中sysinit 指定需要运行的第一个程序(或脚本),boot将在sysinit之后执行,bootwait的执行效果和boot一样,但init会等待该命令结束,其余的action(不含initdefault)都与某个运行级别相关,可以在inittab的man手册中找到各个action的定义的具体描述。
process:给出每行要执行的命令。
知道inittab的字段意义以后,我们来看一下inittab的结构。inittab的第一行是设定系统的运行级别,如下所示:
id:5:initdefault:
从以上的介绍中可以知道,系统的运行级别是5,也就是系统启动时自动进入xwindow。接着进行系统初始化:
si::sysinit:/etc/rc.d/rc.sysinit
这样init进程就会执行/etc/rc.d/rc.sysinit。这个脚本最常见的动作就是激活交换分区,检查磁盘,加载硬件模块,这些动作无论哪个运行级别都是需要优先执行的。只有在rc.sysinit执行完以后init进程才会执行其他的boot或bootwait动作。假如没有boot或bootwait动作,init进程就接着执行下面的内容:
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
这里,系统将会根据运行级别选择要执行的命令。从前面我们已经知道系统的运行级别是5,所以实际上只有这一行被执行了:
l5:5:wait:/etc/rc.d/rc 5
这一行的最后是以参数5运行/etc/rc.d/rc,实际上就是运行/etc/rc.d/rc3.d下的所有程序(或脚本)。用ls命令查看/etc/rc.d/rc3.d目录,如下:
[kj501@c4 kj501]$ ls /etc/rc.d/rc3.d/
K09dm@ S16ypserv@ S56rawdevices@ S90crond@
K55routed@ S17alsa@ S56xinetd@ S90postgresql@
K90mysql@ S18sound@ S60cups@ S91smb@
S01usb@ S20random@ S60nfs@ S92lisa@
S03iptables@ S20xfs@ S60rwhod@ S95innd@
S05harddrake@ S25netfs@ S66yppasswdd@ S95kheader@
S10network@ S26apmd@ S75keytable@ S99devfsd@
S11portmap@ S26ypxfrd@ S80postfix@ S99hamboot@
S12syslog@ S40atd@ S85httpd@ S99linuxconf@
S13partmon@ S40saslauthd@ S85numlock@ S99local@
S14nfslock@ S55named@ S85proftpd@
S15gpm@ S55sshd@ S89internet@
这些文件都是一些系统服务程序,以“S”打头的文件用来在进入运行级别5时启动服务进程,以“K”打头的文件用来在系统关闭,离开运行级别5之前终止服务进程。后面的数字大小决定执行的先后次序,对于以”K“打头的服务进程,按照从高到低的顺序执行,对于以”S“打头的服务进程,按照从低到高的顺序执行。
假如用ls -l 查看这些文件,就会发现它们大部分都是到/etc/rc.d/init.d目录下各个shell脚本的符号链接,而且这些脚本一般能接受start、stop、restart、status等参数,在启动时以start参数启动以“S”打头的脚本链接,假如存在一个同样的脚本链接但是以”K“打头,也会先执行以”K“打头的脚本链接,然后再执行以”S“打头的脚本链接,以保证这个服务进程是重新启动的。要注重在这些脚本的最后,都有一个S99local@的链接,这个链接是链接到/etc/rc.d/rc.local,而不是象其它脚本链接一样链接到/etc/rc.d/init.d下。可以在这个rc.local加入一些每个运行级别都要执行但又必须在系统服务进程启动后才能执行的命令,比如说对一些系统服务程序的参数设置。执行这些脚本之后,下面就要执行一个在每个运行级别都要执行的命令:
ud:nce:/sbin/update
这句话表明每一个运行级别都要运行命令update,此程序每隔30秒把内存缓冲区的内容回写一次,称为"同步",以防止系统崩溃或忽然掉电造成的数据丢失和损坏。在这