分类: LINUX
2009-10-28 06:35:48
5.a. 运行级别
启动你的系统
当你启动你的系统时,你会发现有很多的文字信息输出。如果你仔细观察的话,你会发现这些文字信息每次启动时都一样。这些动作的顺序称作为启动顺序,并且差不多是固定的。
首先,你的启动程序会加载你在启动程序配置文件中定义的内核镜像到内存中,当然这是在CPU运行内核之后。当内核加载和运行后,它初始化所有内核相关的相关工作,并开始进程init。
这个进程然后确认所有的文件系统(在/etc/fstab)中定义的)已经挂载准备使用了。然后它会执行在/etc/init.d里几个脚本,这些脚本按照顺序启动了你需要的一些服务,使得系统成功启动。
最后,所有的脚本执行后,init激活终端(绝大多数情况下是一些虚拟终端,可以用Alt-F1、Alt-F2等激活)并附加了一个叫做agetty的特殊进程。这个进程将确保你可以通过运行login来从这些终端登录。
初始化脚本
现在init不会随机的执行/etc/init.d里的脚本,甚至不会执行/etc/init.d里的所有脚本。它只执行那些让它执行的脚本。这些是由/etc/runlevels来决定要执行的脚本。
首先,init运行所有/etc/init.d里链接到/etc/runlevels/boot的脚本。通常情况下,它会安装字母顺序执行这些脚本,但是有些脚本有依赖性,它们会告诉系统另外的一个脚本必须在它们之前运行。
当所有/etc/runlevels/boot所指向的脚本已经运行,init继续运行和/etc/runlevels/default有符号链接的脚本。同样,它会按照字母顺序执行这些脚本,除非一个脚本有依赖性并提供了一个准确的启动顺序。
Init是怎样工作的?
当然init不会自己决定一切,它需要一个给它指定行为的配置文件,这个配置文件就是/etc/inittab。
如果你还记得我们前面刚解释的启动顺序,你会记得init首先要做的是挂载所有的文件系统。这个是由/etc/inittab里的下面一行定义的:
代码 1: /etc/inittab里的系统初始化行 |
si::sysinit:/sbin/rc sysinit |
这一行告诉init必须去执行/sbin/rc sysinit来初始化系统。/sbin/rc脚本是用来负责初始化的,因此你可能认为init并没有什么可做的,实际上它只不过将初始化系统的任务交付给了另外一个进程。
其次,init执行所有和/etc/runlevels/boot有符号链接的脚本,这个是由下面一行定义的:
代码 2: 系统初始化,继续 |
rc::bootwait:/sbin/rc boot |
同样,脚本rc来执行必要的工作。注意到给rc的参数(boot)就是/etc/runlevels中要使用到的子文件夹。
然后init继续检查它的配置文件,看看还有什么需要runlevel来运行的。要决定这个,它将会读入/etc/inittab里的下面一行:
代码 3: initdefault行 |
id:3:initdefault: |
在这种情况下(绝大部分Gentoo用户将使用的),runlevel的级别为3。根据这个信息,init会查看启动runlevel 3需要运行哪些程序:
代码 4: 运行级别的定义 |
l0:0:wait:/sbin/rc shutdown l1:S1:wait:/sbin/rc single l2:2:wait:/sbin/rc nonetwork l3:3:wait:/sbin/rc default l4:4:wait:/sbin/rc default l5:5:wait:/sbin/rc default l6:6:wait:/sbin/rc reboot |
定义了级别3的行再一次掉用了rc脚本来开始这些服务(现在使用参数default)。同样我们也注意到rc使用的参数是/etc/runlevels里我们要用到的子文件夹。
当rc完成后,init将会决定要激活什么虚拟终端和对每个终端运行什么程序:
代码 5: 虚拟终端的定义 |
c1:12345:respawn:/sbin/agetty 38400 tty1 linux c2:12345:respawn:/sbin/agetty 38400 tty2 linux c3:12345:respawn:/sbin/agetty 38400 tty3 linux c4:12345:respawn:/sbin/agetty 38400 tty4 linux c5:12345:respawn:/sbin/agetty 38400 tty5 linux c6:12345:respawn:/sbin/agetty 38400 tty6 linux |
什么是运行级别?
你应该注意到init使用一种数字策略来决定要激活的运行级别。一个运行级别是一种你系统运行的状态,包含了你进入和退出这个运行级别要执行的一系列的脚本(运行级别脚本或者initscripts)。
在Gentoo里定义了7个运行级别:3个内部运行级别和四个供用户定义的运行级别。这些内部运行级别分别叫做sysinit、shutdown和reboot,所做的就同它们名字一样:初始化系统、关机和重启机器。
用户定义的运行级别都在/etc/runlevels中有个附带的子文件夹:boot、default、nonetwork和single。运行级别boot启动所有其他运行级别要使用的系统服务。其余的三个运行级别主要不同在它们要启动的服务:default是用作日常工作的,nonetwork是在网络不需要的情况下使用,还有single是用来给你修复系统的。
初始化脚本的工作
进程rc启动的脚本都叫做初始化脚本。在/etc/init.d里的每个脚本都可以执行时带上参数start、stop、restart、pause、zap、status、ineed、iuse、needsme、usesme或者broken。
要启动、停止或者重启一个服务(和所有的独立的服务),应该是用到参数start、stop和restart:
代码 6: 启动Postfix |
# /etc/init.d/postfix start |
注释: 只要这个给定服务所需要的服务会停止或这重启,其他的独立的服务(那些使用这个服务,但并不需要它的)将不会改变。 |
如果你要停止一个服务,但不停止那些依赖于它的服务,你可以使用参数pause:
代码 7: 停止Postfix,但是保持依赖于它的服务继续运行 |
# /etc/init.d/postfix pause |
如果你要查看一个服务的状态(启动,停止,暂停……),你可以使用参数status:
代码 8: postfix的状态信息 |
# /etc/init.d/postfix status |
如果状态信息告诉你服务正在运行,但是你知道实际上不是,然后你可以使用参数zap刷新状态信息为“stopped”:
代码 9: 刷新postfix的状态信息 |
# /etc/init.d/postfix zap |
要询问这个服务的依赖性,你可以使用参数iuse或者ineed。使用ineed你可以查看这个服务正常工作真正所需要的服务,而iuse将会显示这个服务要使用到的服务,但并不是为正常工作所必须的。
代码 10: 询问Postfix所必须依赖的服务列表 |
# /etc/init.d/postfix ineed |
同样,你可以询问哪些服务需要这个服务(neesme)或者哪些服务可以用到这个服务(usesme):
代码 11: 询问需要Postfix的服务列表 |
# /etc/init.d/postfix needsme |
最后,你可以询问这个服务所需要的但又缺失的依赖性:
代码 12: 询问Postfix所缺失的依赖性 |
# /etc/init.d/postfix broken |
5.b. rc-update的工作
什么是rc-update?
Gentoo的初始化系统使用依赖树(dependency-tree)来决定什么服务会首先启动。因为这是个很沉闷的工作,我们不会让我们的用户去手动来作,我们创建了简化运行级别和初始化脚本的管理的工具。
使用rc-update你可以从一个运行级别中添加或删除初始化脚本。工具rc-update然后会自动要求depscan.sh脚本来重新创建依赖树。
添加和删除服务
在Gentoo的安装过程中你已经添加启动脚本到“default”运行级别。那个时候你可能还不清楚“default”是用作什么的,但是现在你应该知道了。脚本rc-update需要由第二个参数来决定其行为:add、del或者show。
要添加或删除一个初始化脚本,只需要给rc-update参数add或者del,并随后附上初始化脚本和运行级别。比如:
代码 13: 将Postfix从默认运行级别中删除 |
# rc-update del postfix default |
命令rc-update show将会显示所有已有的初始化脚本,并列出它们在哪个运行级别中运行:
代码 14: 获得初始化脚本的信息 |
# rc-update show |
5.c. 配置服务
为什么需要额外的配置?
初始化脚本有时候很复杂。因此让用户自己编辑初始化脚本没有什么意思,这样会让脚本错误百出。因此很重要的一点就是可以配置这样的一个服务。比如说,你可能想给这个服务更多的选项。
在初始化脚本之外配置的另一个原因也可以是不用担心你的配置在更新初始化脚本时被覆盖。
文件夹/etc/conf.d
Gentoo提供了一个简单的方法来配置这样的一个服务:每一个可以配置的初始化脚本在/etc/conf.d里有一个文件。比如说,apache2的初始化脚本(叫做/etc/init.d/apache2)有一个配置文件叫/etc/conf.d/apache2,它包含了Apache 2服务起启动时你要给它的选项:
代码 15: /etc/conf.d/apache2中定义的变量 |
APACHE2_OPTS="-D PHP4" |
这样的一个配置文件包含了变量并且只有变量(就同/etc/make.conf一样),使得配置服务非常简便。它还允许我们提供更多有关这个变量的信息(以注释形式)。
5.d. 书写初始化脚本
我必须如此吗?
当然不是。自己书写一个脚本通常情况下不是必须的,因为Gentoo给所有提供的服务提供了一个可以使用的初始化脚本。但是,你可能没有通过Portage来安装一个服务,这种情况下你将需要创建一个初始化脚本。
如果服务提供的脚本不是特定写给Gentoo的,不要使用:Gentoo的初始化脚本和其他发行版的初始化脚本不兼容!
布局
一个初始化脚本的基本布局如下。
代码 16: 一个初始化脚本的基本布局 |
#!/sbin/runscript depend() { (依赖性信息) } start() { (启动服务所需的命令) } stop() { (停止服务所需的命令) } restart() { (重启服务所需的命令) } |
每个初始化脚本都需要定义函数start(),其他所有的函数都是可选的。
依赖性
这里有两种依赖性你可以定义:use和need。我们前面提过,依赖性need比use要严格的多。根据依赖性的类型,你需要输入你要依赖的服务或者虚拟依赖性。
虚拟依赖性是由一个服务提供的依赖性,但不仅仅只由那个服务提供。你的初始化脚本可以依赖于一个系统日志服务,但是我们有很多系统日志程序(metalogd、syslog-ng、sysklogd……)。因为你不能同时需要它们中的每一个(没有一个系统会同时安装和运行这些系统日志程序),但我们确认所有的这些服务提供一个虚拟依赖性。
让我们来看看postfix服务的依赖性信息。
代码 17: Postfix的依赖性信息 |
depend() { need net use logger dns provide mta } |
就同你看到的,postfix服务:
控制顺序
在一些情况下你可能并不需要一个服务,但是需要你的服务在另一个在系统中存在的服务(注意条件,这里再也没有别的依赖性)前面(或者后面)启动过,并且这两个服务同时在相同的运行级别中(注意条件,只有在同一运行级别的服务才包括)。你可以使用before或者after设置来提供这个信息。
作为一个例子,我们来看看服务Portmap的设置:
代码 18: The depend() function in the Portmap service |
depend() { need net before inetd before xinetd } |
你也可以使用“*”来代替相同运行级别的所有服务,但是这个不是推荐使用。
代码 19: 在运行级别中第一个运行的初始化脚本 |
depend() { before * } |
标准函数
紧随函数depend,你也需要定义函数start。这个包含了所有初始化这个服务所需要的命令。我们推荐使用函数ebegin和eend来告诉用户所发生的事情:
代码 20: 函数start()样例 |
start() { ebegin "Starting my_service" start-stop-daemon --start --quiet --exec /path/to/my_service eend $? } |
如果你需要有关函数start()的更多例子,请阅读在你的文件夹/etc/init.d里已有的初始化脚本。对于start-stop-daemon,如果你需要更多的信息,这里提供了一个非常好的手册:
代码 21: 获得start-stop-daemon的手册 |
# man start-stop-daemon |
其他你可以定义的函数有stop()和restart。你不是必须要定义这些函数!如果你使用start-stop-daemon,我们的初始化系统有足够的能力来自动完成这些函数。
添加自定义的选项
如果你想你的初始化脚本支持更多的选项,而不仅仅是我们已经遇到过的那些,你可以添加这些选项到变量opts,并且创建一个和这个选项同名的函数。比如,要支持一个名为restartdelay的选项:
代码 22: 支持选项restartdelay |
opts="${opts} restartdelay" restartdelay() { stop() sleep 3 # 在再次启动前等候三秒钟 start() } |
服务配置中的变量
对于支持/etc/conf.d里的配置文件,你不需要作任何事情:如果你的初始化脚本执行,下面的文件将会自动读取(比如可以使用的变量):
还有,如果你的初始化脚本提供了一个虚拟依赖性(如net),和这个依赖性相关的文件(如/etc/conf.d/net也会被读取。
5.e. 改变运行级别的行为
谁将得益于此?
许多笔记本用户了解这个情况:在家时你需要启动net.eth0,而在路上你就不需要启动net.eth0(因为没有网络可用)。对于Gentoo,你可以根据你的意愿来改变运行级别的行为。
比如,你可以创建另一个“default”运行级别,联系有其他的初始化脚本。然后在启动时你可以选择你要使用哪个default运行级别。
使用SOFTLEVEL
首先,给你的另一个“default”运行级别创建文件夹,作为一个例子我们创建offline运行级别:
代码 23: 创建一个运行级别文件夹 |
# mkdir /etc/runlevels/offline |
给新建的运行级别添加必须的初始化脚本。比如,如果你需要一个同你当前的default运行级别一模一样的设置,只是没有net.eth0:
代码 24: 添加必须的初始化脚本 |
# ls /etc/runlevels/default acpid domainname local net.eth0 netmount postfix syslog-ng vixie-cron # rc-update add acpid offline # rc-update add domainname offline # rc-update add local offline # rc-update add syslog-ng offline # rc-update add vixie-cron offline |
现在编辑你的系统启动程序配置文件,并为offline运行级别添加一个新的记录。比如,在/boot/grub/grub.conf:
代码 25: 给offline运行级别添加一条记录 |
title Gentoo Linux Offline Usage root (hd0,0) kernel (hd0,0)/kernel-2.4.25 root=/dev/hda3 softlevel=offline |
现在所有的都设置好了。如果你启动你的系统并在启动时选择新添的记录,将会使用运行级别offline而不是default。
使用BOOTLEVEL
使用bootlevel和softlevel几乎完全相似。唯一的不同是你定义一个新的“boot”运行级别而不是新的“default”运行级别。