通过并行化 Linux 系统服务来提高引导速度
时间:2005-03-28 来源:loverf
通过并行化 Linux 系统服务来提高引导速度
级别:中级
James Hunt (jameshunt-at-uk.ibm.com)
软件工程师,IBM
2003 年 11 月
本文向您描述了如何在不影响可用性的前提下加快 Linux 操作系统的引导速度。当然,这种方法要求您对系统服务及服务之间的依赖关系有所理解,如果它们可以并行启动,就让它们并行启动而不是串行启动。
毫无疑问 Linux 是一个优秀的系统,但仍然无法摆脱一个常见的责难(尤其是来自具有 Microsoft Windows 背景的人),那就是 Linux 系统从按下“on”键开始到可以使用,需要的时间太长。其实他们说的没错,Linux 确实需要比较长的引导时间。
在这里我所描述的加快 Linux 引导速度的技术虽然理解起来很简单,但真正实现却需要谨慎行事。我希望 Linux 的发行商能采用这种方法,这样用户就可以省去那些配置任务。不过如果您喜欢冒险,请继续阅读本文。
写在开始之前
如果您想体验一下这种方法,您首先必须得熟悉 Linux 的配置脚本。修改系统的启动设置可能会带来危险,甚至可能会导致您的系统无法启动。如果出现这种情况,请重新启动机器并进入单一用户模式(运行级1),把您所做的修改还原回来,然后再重新启动。永远记住要备份您所修改过的所有文件,为了防止最坏的情况发生,您还需要有至少一个系统备份的映像。
我强烈建议您在考虑用我所建议的方法修改一个正式的系统之前,先去修改一个无关紧要的测试系统。如果您只有一台机器,那么您可以使用 UML (User Mode Linux) 这一非常有用的工具。UML是一个内核补丁,它可以将Linux内核编译成为一个二进制文件,然后您可以像运行一个普通的程序一样去运行这个内核。也就是说,您可以在您的正常的系统之上以一个进程的方式来运行一个完整的 Linux 系统。您可以将其想象为在一个正常的系统中运行一个 Linux 系统。(请参阅本文末尾的参考资料,可以找到可以下载UML的站点以及 developerWorks 网站上关于UML的教程)。
使用UML您可以工作于一个测试系统,哪怕把这个测试系统完全破坏掉,也不会影响您正常的系统。
概述
本文的第一部分介绍当 Linux 内核(Linux 机器的的“核心”)加载后,一个 Linux 系统怎样在后台启动。然后介绍加快您的系统引导速度的技术。
如果您对运行级和服务启动脚本已经熟悉,您可能希望直接跳转到传统服务框架的局限。
Linux 引导次序和运行级
一个 Linux 系统的引导过程可以分为几个阶段。本文并不会解释所有的不同阶段,因为我们所关心只是当内核加载后的那一个阶段。
您可以运行 /sbin/runlevel 命令来确定您的系统当前的运行级。(更多详细信息请查阅 man runlevel)。
当内核被加载并开始运行时会调用 /sbin/init 程序。这个程序以 root 身份运行,并且在开始引导时按照要求设定为“运行级”。(更多关于 init 程序的详细信息,请参考 man init)
什么是运行级?
一个运行级仅仅是一个数字,Linux根据这个数字来区分不同类型的高层次配置,系统将按照不同的高层次配置来进行引导。由于绝大部分运行级数字都定义了明确的含义,因而它们基本上是“众所周知”的。Red Hat Linux 系统的主要运行级见表1。
表 1. Red Hat Linux运行级
运行级 说明
0 关闭
1 单一用户模式(一般仅用于管理目的)
2 多用户模式,不允许使用网络
3 多用户模式,允许使用网络
4 没有用到的运行级
5 多用户模式,允许使用网络,X-Windows 方式(图形登录界面)
6 重新引导
init如何初始化系统
init 通过一个ASCII配置文件(/etc/inittab)来确定如何改变运行级。通常,init 会根据这个配置文件去运行 /etc/rc.d/rc 脚本,并将运行级数字传递给这一脚本。
rc.sysinit 脚本
在 Red Hat 系统中,在运行 rc 脚本之前,init 将首先运行 /etc/rc.d/rc.sysinit 脚本,这个脚本执行那些必需的底层设置任务,比如设置系统时钟,检查磁盘错误,然后挂载文件系统。
在本文看来,正是从运行 rc 脚本开始,事情才变得有趣。
系统服务
rc 脚本负责启动用户需要的所有服务。就像名字所描述的一样,所谓服务就是系统提供的有用的工具。可能会有很多服务需要启动。大部分的 Linux 系统会启动 sshd(安全Shell服务)、syslog(系统日志工具)和 lpd(打印服务),但还会有更多的服务需要启动。比如,我的 Red Hat 9 系统现在运行着29个服务,但如果我把所有的服务都启动,那么我的系统中将会有近50服务在运行。
还有一点很重要,我们应该明白有的服务可能只能由特定的运行级来启动。比如,除了运行级5(多用户图形方式)以外,几乎不会启动某种形式的图形服务,因为其它所有的运行级都是非图形方式的。接下来我们将深入讨论这一问题。
服务程序在哪里?
可选的服务程序目录
在一些 Linux 系统中,服务程序有时候是在 /etc/init.d 目录下。
通常在 /etc/rc.d/init.d/ 目录下可以找到服务程序。
如果你浏览一下这个目录,你就会发现相当多的(如果不是全部都是的话)服务程序实际上都是 shell 脚本,用于调用其他程序完成实际的工作。
rc 脚本如何知道在每个运行级下去运行哪些脚本?
回顾一下,如果我们不希望在某个运行级下运行某个脚本,我们如何告诉系统这样去做?答案是在 /etc/rc.d/ 目录下,在这个目录下,除了我们已经讨论过的 init.d/ 目录以外,还有一组目录,每一个目录对应一个运行级。这些目录以 rc<runlevel>.d 的形式来命名,比如,对应运行级5的目录为 /etc/rc.d/rc5.d/ 。在这些rc.d目录中,每一个目录下都有一组符号链接,指向 /etc/rc.d/init.d 中的真正的服务程序。实际上,后边我们会发现,每个服务事实上有两个符号链接。
服务链接名
这些指向实际服务程序的符号链接的名字很重要,它们遵循严格的命名约定,这样 rc 脚本就知道如何处理它们。
为了便于标识,每个链接的名字都以它们所指向的服务的名字做为后缀。
前缀由两部分构成:一个大写字母,紧跟着是一个两位的十进制数。前缀中的大写字母是“S”(表示“启动”),或者“K”(表示“杀死”,或者停止)。两位数的大小范围是自00到99。
服务链接名正则表达式
符号链接的名字可以用 egrep 正则表达式来描述,[SK][0-9]{2}[a-zA-Z]+。(更多详细信息请参阅 man egrep)。
启动和停止服务
如果我们决定让 Linux 机器引导到图形模式(运行级5),当 init 调用 rc 脚本并传递给它运行级数字时,rc 脚本将到 /etc/rc.d/rc5.d/ 中查找,并且去运行它所能找到的所有符号链接(也就是说,它将运行每个链接指向的程序/脚本)。它将在两个截然不同的阶段来运行这些链接;首先它会执行所有以“K”开头的链接,同时传递给它们参数“stop”。执行完以后,所有这些链接指向的服务都被停止。
当 rc 脚本把所有需要停止的服务都停止后,它将去执行所有以“S”开头的链接,同时传递给它们参数“start”。执行完以后,所以这些链接指向的服务都被启动。rc 脚本也把参数“start”传递给每一个程序。
rc 把参数“tart”或者“stop”传递给每一个服务程序,这样做是为了只用一个服务程序可以启动或停止那个服务——服务程序根据传递给它的参数值分辨系统是正在引导还是正在关闭。
有一个重要的方面我还没有解释——链接名的数字部分。在“S”或者“K”之后的两位十进制数是 rc 脚本用来确定启动链接(就是链接指向的服务)的顺序的。数字较小(比如00,01,等等)的链接在数字较大(99是最大的)链接之前运行。我们会在本文后边的内容中再次提到这一重点问题。
现在还迷惑吗?清单1列出了运行级5对应目录下的所有链接。当引导到运行级5的时候,最先被执行的链接将是 K05saslauthd,因为它以“K”开头,并且在所有的以“K”开头的链接中两位十进制数是最小。最先被执行的启动链接将是 S05kudzu,因为它以“S”开头,并且在所有以“S”开头的链接中两位十进制数是最小的。最后一个运行的链接将是 S99local。
清单 1. 运行级5的指向服务程序的链接
# cd /etc/rc.d/rc5.d
# ls -al
total 8
drwxr-xr-x 2 root root 4096 Jul 15 09:29 .
drwxr-xr-x 10 root root 4096 Jun 21 08:52 ..
lrwxrwxrwx 1 root root 19 Jan 1 2000 K05saslauthd -> ../init.d/saslauthd
lrwxrwxrwx 1 root root 20 Feb 1 2003 K15postgresql -> ../init.d/postgresql
lrwxrwxrwx 1 root root 13 Jan 1 2000 K20nfs -> ../init.d/nfs
lrwxrwxrwx 1 root root 14 Jan 1 2000 K24irda -> ../init.d/irda
lrwxrwxrwx 1 root root 17 Jan 1 2000 K35winbind -> ../init.d/winbind
lrwxrwxrwx 1 root root 15 Jan 1 2000 K50snmpd -> ../init.d/snmpd
lrwxrwxrwx 1 root root 19 Jan 1 2000 K50snmptrapd -> ../init.d/snmptrapd
lrwxrwxrwx 1 root root 16 Jun 21 09:43 K50vsftpd -> ../init.d/vsftpd
lrwxrwxrwx 1 root root 16 Jun 21 08:57 K73ypbind -> ../init.d/ypbind
lrwxrwxrwx 1 root root 14 Jun 21 08:54 K74nscd -> ../init.d/nscd
lrwxrwxrwx 1 root root 18 Feb 8 11:15 K92iptables -> ../init.d/iptables
lrwxrwxrwx 1 root root 19 Feb 1 2003 K95firstboot -> ../init.d/firstboot
lrwxrwxrwx 1 root root 15 Jan 1 2000 S05kudzu -> ../init.d/kudzu
lrwxrwxrwx 1 root root 14 Jun 21 08:55 S09isdn -> ../init.d/isdn
lrwxrwxrwx 1 root root 17 Jan 1 2000 S10network -> ../init.d/network
lrwxrwxrwx 1 root root 16 Jan 1 2000 S12syslog -> ../init.d/syslog
lrwxrwxrwx 1 root root 17 Jan 1 2000 S13portmap -> ../init.d/portmap
lrwxrwxrwx 1 root root 17 Jan 1 2000 S14nfslock -> ../init.d/nfslock
lrwxrwxrwx 1 root root 18 Jan 1 2000 S17keytable -> ../init.d/keytable
lrwxrwxrwx 1 root root 16 Jan 1 2000 S20random -> ../init.d/random
lrwxrwxrwx 1 root root 16 Jun 21 08:52 S24pcmcia -> ../init.d/pcmcia
lrwxrwxrwx 1 root root 15 Jan 1 2000 S25netfs -> ../init.d/netfs
lrwxrwxrwx 1 root root 14 Jan 1 2000 S26apmd -> ../init.d/apmd
lrwxrwxrwx 1 root root 16 Jan 1 2000 S28autofs -> ../init.d/autofs
lrwxrwxrwx 1 root root 14 Jan 1 2000 S55sshd -> ../init.d/sshd
lrwxrwxrwx 1 root root 20 Jan 1 2000 S56rawdevices -> ../init.d/rawdevices
lrwxrwxrwx 1 root root 16 Jan 1 2000 S56xinetd -> ../init.d/xinetd
lrwxrwxrwx 1 root root 14 Feb 1 2003 S58ntpd -> ../init.d/ntpd
lrwxrwxrwx 1 root root 13 Jun 21 10:42 S60afs -> ../init.d/afs
lrwxrwxrwx 1 root root 13 Jan 1 2000 S60lpd -> ../init.d/lpd
lrwxrwxrwx 1 root root 16 Feb 8 17:26 S78mysqld -> ../init.d/mysqld
lrwxrwxrwx 1 root root 18 Jan 1 2000 S80sendmail -> ../init.d/sendmail
lrwxrwxrwx 1 root root 13 Jan 1 2000 S85gpm -> ../init.d/gpm
lrwxrwxrwx 1 root root 15 Mar 22 08:24 S85httpd -> ../init.d/httpd
lrwxrwxrwx 1 root root 15 Jan 1 2000 S90crond -> ../init.d/crond
lrwxrwxrwx 1 root root 13 Jan 1 2000 S90xfs -> ../init.d/xfs
lrwxrwxrwx 1 root root 17 Jan 1 2000 S95anacron -> ../init.d/anacron
lrwxrwxrwx 1 root root 13 Jan 1 2000 S95atd -> ../init.d/atd
lrwxrwxrwx 1 root root 15 Jun 21 08:57 S97rhnsd -> ../init.d/rhnsd
lrwxrwxrwx 1 root root 14 Jul 15 09:29 S98wine -> ../init.d/wine
lrwxrwxrwx 1 root root 13 Feb 8 17:26 S99db2 -> ../init.d/db2
lrwxrwxrwx 1 root root 11 Jun 21 08:52 S99local -> ../rc.local
#
这看起来好象是非常复杂的系统,但实际上它提供了极好的灵活性,因为如果您想临时禁止某个特定运行级中的服务,只要把适当的符号链接删除即可。不过,手工管理这些链接可能会让人感觉厌烦,并且容易出错(尤其当您累了的时候),所以可以采用一个相对好一些的方法,使用 chkconfig 命令。
chkconfig 和 xinetd
如果您有一个新版本的 chkconfig ,您会在主输出的最后一部分看到有关 xinetd (Internet services daemon)的配置。为了减化说明,此部分没有列入清单2中。
如何找出激活的服务
想查看您已经激活了多少服务,运行这个命令:
/sbin/chkconfig --list
清单 2 列出了这个命令的输出。您可以看到,每一行有八列。
chkconfig 命令还可以用来切换任何一个服务的开或关。详细信息请参考手册页(man chkconfig)。
清单 2. chkconfig --list|sort的输出
afs 0ff 1ff 2ff 3n 4ff 5n 6ff
anacron 0ff 1ff 2n 3n 4n 5n 6ff
apmd 0ff 1ff 2n 3n 4n 5n 6ff
atd 0ff 1ff 2ff 3n 4n 5n 6ff
autofs 0ff 1ff 2ff 3n 4n 5n 6ff
crond 0ff 1ff 2n 3n 4n 5n 6ff
db2 0ff 1ff 2ff 3n 4ff 5n 6ff
firstboot 0ff 1ff 2ff 3ff 4ff 5ff 6ff
gpm 0ff 1ff 2n 3n 4n 5n 6ff
httpd 0ff 1ff 2ff 3ff 4ff 5n 6ff
iptables 0ff 1ff 2ff 3ff 4ff 5ff 6ff
irda 0ff 1ff 2ff 3ff 4ff 5ff 6ff
isdn 0ff 1ff 2n 3n 4n 5n 6ff
keytable 0ff 1n 2n 3n 4n 5n 6ff
kudzu 0ff 1ff 2ff 3n 4n 5n 6ff
lpd 0ff 1ff 2n 3n 4n 5n 6ff
mysqld 0ff 1ff 2ff 3n 4ff 5n 6ff
netfs 0ff 1ff 2ff 3n 4n 5n 6ff
network 0ff 1ff 2n 3n 4n 5n 6ff
nfs 0ff 1ff 2ff 3ff 4ff 5ff 6ff
nfslock 0ff 1ff 2ff 3n 4n 5n 6ff
nscd 0ff 1ff 2ff 3ff 4ff 5ff 6ff
ntpd 0ff 1ff 2ff 3n 4ff 5n 6ff
pcmcia 0ff 1ff 2n 3n 4n 5n 6ff
portmap 0ff 1ff 2ff 3n 4n 5n 6ff
postgresql 0ff 1ff 2ff 3ff 4ff 5ff 6ff
random 0ff 1ff 2n 3n 4n 5n 6ff
rawdevices 0ff 1ff 2ff 3n 4n 5n 6ff
rhnsd 0ff 1ff 2ff 3n 4n 5n 6ff
saslauthd 0ff 1ff 2ff 3ff 4ff 5ff 6ff
sendmail 0ff 1ff 2n 3n 4n 5n 6ff
snmpd 0ff 1ff 2ff 3ff 4ff 5ff 6ff
snmptrapd 0ff 1ff 2ff 3ff 4ff 5ff 6ff
sshd 0ff 1ff 2n 3n 4n 5n 6ff
syslog 0ff 1ff 2n 3n 4n 5n 6ff
vsftpd 0ff 1ff 2ff 3ff 4ff 5ff 6ff
winbind 0ff 1ff 2ff 3ff 4ff 5ff 6ff
wine 0ff 1ff 2n 3n 4n 5n 6ff
xfs 0ff 1ff 2n 3n 4n 5n 6ff
xinetd 0ff 1ff 2ff 3n 4n 5n 6ff
ypbind 0ff 1ff 2ff 3ff 4ff 5ff 6ff
清单 2中第一列是服务的名字,接下来的列是运行级和每一个运行级中服务的状态。例如,ntpd (Network time daemon)服务被配置为只在运行级3中(多用户,无图形)和运行级5(多用户,有图形)中启动,sshd 服务在运行级2,3,4和5中都被切换到开的状态。
注意在运行级0和6中没有一个服务要启动。回顾表 1,原因显而易见。运行级1表示要关闭或停止系统,因此在机器将要“关闭”时,您不会想要启动任何服务。运行级6中也是如此。
运行级1——“单一用户模式”——是一个特别的运行级,一般在系统出问题的时候使用。一直以来,在运行级1中运行的唯一一个应用程序是 shell,允许超级用户来修复系统或者让超级用户在一个安全的环境中修改系统。这样是安全的——就像它的名字“单一用户模式”的含意一样——只有超级用户可以访问系统。并且,联网是禁用的,所以没有人可以远程登录。如表 1所示,单一用户模式中运行的唯一一个服务是keytable,这样使得超级用户的键盘可以正常使用。
激活服务与运行服务的对比
有时服务会由于某种原因无法启动,用下面这个命令查看当前有哪些服务正在运行:
/sbin/service --status-all
这个命令将为每个服务输出一行或多行,指出每个服务是否在运行,如果在运行,则列出服务的一些特定的输出,比如服务运行的PID(进程号)。service 命令没有手册页,但是您可以在运行这个命令时使用--help 选项,您就可以得到有关它的操作的一些帮助信息。
传统服务框架的缺陷
关键的是,只有当配置中的所有服务都启动以后,您才可以登录进入您的 Linux 系统。等待50个服务启动可能会需要若干分钟,而这本来应该是您享用Linux系统的时间。
我已经找到了一个加速这个过程的方法。注意这种方法不会停止任何服务。不管怎样,停掉那些不用的服务是很明智的,不仅是因为这样可以加快引导的速度(在机器可以登录之前需要运行的服务少了),而且,由于很多服务要以 root 用户身份来运行,停掉不用的服务会减少您的安全隐患。
扼要重述一下,当一个 Linux 系统引导时,它以一种连续的方式来运行所有的某个运行级所配置的所有服务——一个接一个地。这是一个耗时的操作。
或许一个很明显的加快服务启动速度的方法是并行地启动所有的服务,这样它们就可以同时启动。不幸的是,虽然这听起来很吸引人,却不可行。原因是各个服务之间存在依赖的关系。Linux没有把这些依赖关系完全显式地表示出来,但是事实上这些依赖关系是存在的。还记得我们先前讨论的关于链接名字格式的问题吗?在 “S”和“K”之后的两位数决定了链接(也就是它们指向的服务)的运行顺序。这些数字确定了一个硬性的顺序,这样一定程度上也强化了服务之间的依赖关系。
服务之间的依赖关系
回顾清单 1,我们可以看到 network 服务(S10network)将在ntpd服务(S58ntpd)之前运行。这是我们所期望的,因为 ntpd 服务要求网络可达,以使它可以连接一个本地时间服务器。不幸的是,这个硬性的顺序并不能告诉我们足够的信息,并且会让人误解。例如,在清单 1中我们可以看到 lpd 服务(S60lpd)将在 network 服务之后运行。虽然这样对那些连接到网络并且使用网络打印机的 Linux 系统来说是正确的,但是这并不说明当背板上有一个 inkjet 打印机连接到本地系统时,lpd 服务还是必须要在 network 服务之后运行。实际上,在这种情况下,在启动 network 之前先启动 lpd 会更好一些。
再来看另外一个例子:crond (cron daemon)服务 (在清单 1中的 S90crond)也是在 network 启动之后运行。可是,如果您没有使用远程机器文件的 cron 文件,那么就应该让 crond 在 network 之前启动。
由于我刚才介绍的 Linux 下启动服务的传统方法有一定的局限性,往往倾向于“安全第一",让所有的重要的服务先启动,然后再启动余下的那些。
所以,尽管我们不能并行地启动所有的服务,但我们可以并行地启动那些相互间没有依赖关系的服务。当这些相互间无依赖的服务启动以后,我们可以启动那些所有依赖条件已经满足(也就是说,那些服务所依赖的服务已经启动)的服务。然后重复这一过程,直到所有服务全部启动。
这个看起来是一个复杂的问题,不过幸运的是,已经有一个现成的可以用来解决这个问题的程序。这个程序不是别的,正是 make。
通常当编译软件时,make 会提供我们所需要的严密的框架。所有我们要做的就是告诉 make 什么是服务之间的依赖;它可以去做所有的计算交叉依赖的艰难工作,并且,使用它的鲜为人知的标记 -j ,它可以作为许多"作业"而同步运行。
得出服务间依赖关系
如先前我间接提到的,传统的 Linux 系统没有显式地表示服务间的依赖关系,所以现在我们不得不自己去做一些艰难的工作,得出这些依赖关系。这可能会需要一段时间,因为您可能都不知道每个服务在做什么,更别提服务之间的关系了。然而,如果您没有完成这些工作,这种方法对您来说没有任何益处。(如前面所提到的,如果这种方法有实用价值,希望 Linux 发行商可以采用它,并且为我们做这些艰难的工作。)
认识您的服务
如果当您运行命令 /sbin/chkconfig --list时,您有可能会遇到一些您所不认识的服务,那么花一些时间去弄明白它。一个简单的方法是读那些控制相应服务的脚本中开头处的注释。
这样您就可以把不用的那些工具所对应的服务关掉。即便是您需要它,这样您也可以更加了解您的系统。
现在,我们来做一个简单的实例。我们都知道,ntpd 服务需要网络,这说明 ntpd 服务依赖于network 服务。在 make 语法中这个依赖关系这样表示:
ntpd : network
我们还可以确定 netfs 服务(挂载我们所需要的所有NFS目录)依赖于网络。在我的系统(您的可能会不一样)上,autofs 服务(自动挂载网络文件系统)也依赖于 network 服务,因为我曾经自动挂载远程文件系统(您可能挂载光驱或者软驱)。我们的“依赖表”现在是这样:
ntpd : network
netfs : network
autofs : network
这看起来没什么,但是您知道这意味着什么吗?这意味着一旦 network 服务启动完成,我们可以并行地启动 ntpd,netfs 和 autofs 服务。
做为一个特定的例子,假设所有的服务都需要10秒才能启动。用传统的服务启动方法,启动 network,ntpd,netfs 和 autofs 服务需要40秒。而用这种技术,只需要20秒——节约的50%的时间。
为什么会这样?好了请看,network 服务启动需要10秒时间,但是(因为当 rc 脚本在运行时,机器处于完全多任务的状态)其余三个服务可以同时启动,所以这三个服务合起来的启动时间是10秒。
事实上,大部分服务需要的启动时间可能不是10秒,但是既然每一个服务要做一些完全不同的事情,启动它们所需要的时间会很可观。
样例实现
我在参考资料部分提供的压缩文件中有一个使用上述技术的样例实现。包括一个修改过的用于调用 make 命令的 rc 脚本,以及样例GNU makefile文件,分别是 runlevel.mk,start5.mk和stop5.mk。makefile 文件 runlevel.mk 是控制程序,start5.mk 文件和stop5.mk 文件分别是运行级5时启动和停止服务时的服务依赖描述文件。
注意所给出的启动和停止 makefile 文件提供的不是完全的服务间依赖关系列表,而仅仅是一个例子。同时也要注意,如果您不修改这些文件就在您的系统上使用,几乎不可能成功,因为您的服务列表可能和我的并不一样。
结束语(以及一些补充说明)
我提出了一种用来加快 Linux 机器引导速度的方法。这种方法允许系统在启动服务时启动顺序中靠后的部分服务并行启动,而不是以传统的串行方式启动,以实现引导的加速。这种方法在理论上没有问题,并且可以利用现有的系统工具实现。
这种方法的效率取决于需要启动的服务的数量和每个服务启动所需要的时间。并行的可行性主要取决于服务间的依赖关系。对于某些系统来说,使用这种方法可能只会有很小的改进,但对于其他系统,它可能会显著地影响引导速度。可以这样理解,每个系统都有不同的一组服务被激活,并且每一个服务需要不同的时间来启动。再强调一次,要使用这种方法,您需要确定您的特定系统的服务之间的依赖关系。
补充说明:
* 一些服务程序仅仅是在后台运行一个程序,它们自己就退出了(也就是说,服务程序结束了,但是真实的工作仍然在后台进行)。这说明了一个事实,那就是传统的系统是不完善的,这种服务程序编写者试图在现有框架的界限内减少一些时钟周期。采用本文描述的这种方法将会使依赖关系更加显式化,不需要服务编写者再去“欺骗”。这种方法考虑到在这些服务程序之外建立一个更为高效的框架。
* 当您希望“交互式”引导您的系统时,这里所提到的技术不再适用,因为您通常是当系统某些地方出错的时候才会这样去做;在这种情况下,您可能希望串行地去启动所有的服务以找出出错的原因。不过,修改系统的启动过程,来让用户在系统引导来选择是以串行的方式(允许交互的服务启动)或者“并行”的服务启动方式,是容易实现的。
* 采用这种方法可能还需要更深入的考虑,因为如果传统的系统和新的系统都提供给用户,将需要同步维持两组关于服务如何启动的信息(有序的 rc.d/ 链接文件和运行级 make 文件)。一个更好一些的解决方案是 Linux 发行版本能从 makefile 文件自动生成链接文件,因为 makefile 文件比链接文件记录了更多的关于服务的信息。
* 这个系统可能对一个专用的服务器来说并不适合,因为当一个服务发生错误时,管理员希望能在错误发生时在控制台中马上可以看到这个错误。不过,对于普通的终端用户来说,并行化的方法可以在允许用户查看是否有问题发生的前提下显著地加快引导速度。
* 有趣的是,尽管我提出的这种方法从传统观点来看不是“类似Linux的”,但 Linux 基础标准 (LSB,Linux Standards Base)看起来并没有要指定 init.d 脚本的运行顺序,所以这种方法有可能被 Linux 发行商所采用,而且使之仍然符合 LSB。这对用户来说是一个好消息,如前面所提到的,因为发行商可以为我们计算出所有的软件包之间的依赖关系。
* 有一种方法可能更为大胆,那就是 /etc/inittab 文件中的“action field”的“wait”修改为“once”。这样用户在服务启动完成之前就可以登录。不过,这已经超出了本文的范围。要得到更详细的信息请查阅 man inittab,并且请记住,UML是您的好帮手。
参考资料
* 下载用本文所讨论的方法实现的一个样例的压缩包。
* 官方的 GNU Make 主页中有 make 程序的下载及 make 文档的链接。
* LSB (Linux Standard Base) 的目标是明确定义 Linux 系统的构成。LSB 公共文档中包含了有关“系统初始化”的文档。
* UML (User Mode Linux) 主页中有 UML 的下载及其文档的链接,使用UML您可以在真实的 Linux 系统上再启动一个“虚拟的”Linux 系统。
* 如果需要一个完全的指南来帮助您开始使用 UML, 请参阅教程“用户模式 Linux 简介” (developerWorks, 2003年1月)。
* DeveloperWorks Linux 专区有更多的为 Linux 开发人员准备的资源。
关于作者
James Hunt 是 英国 IBM Hursley 实验室的一名软件工程师。他不是一名赛车手,他的汽车也显然不是赛车。James 自己公开承认是一个Linux 迷,他一有机会就向其他的人来讲述相关的话题,并以此度过了很多美好的时光。他的工作内容是分布式平台(AIX,HP-UX,Linux,OS/400,Solaris和Windows)上的 WebSphere MQ 产品。他曾经做过 UNIX 系统管理员、数据库的管理和编程。他对 Linux 的文件系统和编译器感兴趣,并且他可能是现在仅有的 Perl 编码标准文档的作者。James 是一个古怪的人,喜欢写技术文档。在他的业余时间,他弹吉他,学瑜珈,开游艇,虽然他还未能设法把这些活动并行化。您可以通过 jameshunt-at-uk.ibm.com 与 James 联系。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[color=red][size=24]外一篇[/color][/size]
其中作者特别指出注意的目录,我已看过,红旗4。1下都有,但作者选的是3,而以图型界面登录应该选5。
作者:6692975
并行启动服务加快系统启动速度
今天无意间看到一篇使系统服务并行运行加快系统启动速度的文章,于是
尝试了下,感觉办法很不错,同时让我对lfs的系统引导脚本有了更深的理解
现在系统从加载内核到login只要5秒,飞一样的感觉 ^_^
Ok ! Let`s Go !
因为要修改系统启动的脚本,很可能让系统启动不正常,所以最好有双linux系统,即使修改错误了
还可以从另一个系统引导修复。
如果很不幸,没有的话,可以先学下这招,紧急救护系统也有用 :p
紧急救援模式:
grub菜单中选择linux,按e,e,进入编辑模式,kernel......那一行最后加上
init=/bin/bash,这样引导系统可以得到一个bash shell
这样进入的系统会是read-only的,首先先使用fsck检查系统
fsck -a /dev/hdaX 处理根分区
fsck -R -A -a 处理其他分区
如果系统在上次重启,是正常重启的,文件系统是正常卸载的,
可以不用fsck检查直接把系统挂载成read-write的
mount / -o remount,rw
mount /proc
swap on -a 打开所有的交换分区
在修复好系统后将系统重新挂载成ro的,就可以安全重启了。
mount / -o remount,ro
简单的说一下linux系统的启动过程。
当grub加载内核后,内核执行/sbin/init,init程序读取/etc/inittab内容,开始系统的初始化。
init读取initdefault字段,取得系统运行级别
id:3:initdefault: 这里启动级别是3,通常是多用户字符登陆模式
init读取sysinit字段,开始系统基本的初始化
si::sysinit:/etc/rc.d/init.d/rc sysinit
这一行表示init会去执行/etc/rc.d/init.d/rc这个文件,sysinit是传入的参数,这里要做的是:
挂载proc系统拉,检查根分区拉,开启swap拉。等等
但是在不同的发行版可能不同,比如在Mandrakelinux中是:si::sysinit:/etc/rc.d/rc.sysinit
这样初始化的脚本就成了/etc/rc.d/rc.sysinit
init读取wait字段,开始系统服务初始化
l3:3:wait:/etc/rc.d/init.d/rc 3
经过基本初始化后,init读取run-level中的脚本,这些脚本位于
/etc/rc.d/rcX.d目录下。X是当前的运行级别.这里是系统启动的服务,比如network,alsa,httpd....等
/etc/rc.d/init.d/rc接收一个运行级别作为参数,然后逐一的开启/关闭/etc/rc.d/rcX.d目录下的脚本。
wait表示init会等待它结束再去执行其他程序。
有些发行版在执行rcX.d之后去执行/etc/rc.d/rc.local
最后就看到可爱的login拉
在/etc/rc.d/rcX.d中的脚本都是以"SXX+服务名"或者"KXX+服务名"组成的
并且都是到/etc/rc.d/init.d中相应脚本的符号链接,有的发行版是/etc/init.d,只是位置不同而已。
其中XX是0-9的数字,数字越小,则启动的时间越早。
以S开头的表示系统启动时传递start参数的服务,就是开启拉。K开头的就是
传递stop参数。
/etc/rc.d/rc3.d:
S10sysklogd@ S20network@ S25random@ S30httpd S40alsa@ S85numlock@
可以看出,我的系统进入rc3.d时首先启动的服务是sysklogd,最后是numlock
很明显,httpd服务必须要在network之后运行,不然没有网络哪来的web服务?
在lfs中是通过ls -v 列出它们,然后逐一的执行它们,这样就会使系统启动的速度很慢,服务越多越明显
现在希望做的就是:
让那些相互之间没有依赖关系的服务可以同时开启,而不是逐一的执行
这样系统启动的速度就会大大的提高了。
make 就是实现这个功能的工具。
相互有依赖关系的服务,让make去解决它们的依赖性。
再使用make -j 参数使服务可以并行启动。
在makefile中写入服务之间的相依赖关系:
httpd : network
这样就表示httpd依赖network,当network启动完毕,就可以立刻启动httpd
而其他不相依赖的服务列在makefile中,同时开启。
原理就是这样,下面是我的实际做法:
首先是写make的配置文件,该文件的样例在本文的结尾可以找到.
/etc/rc.d/runlevel.mk
########################################################################
# Description : Gnu Makefile to control the services in the specified
# runlevel. It will run the required services, and log
# the output of the services to the file
# /var/log/initd.start (for service startup) and
# /var/log/initd.stop (for service shutdown).
#
# This controlling program is designed to be invoked by
# the "/etc/rc.d/rc" script.
#
# Author : [email protected]
#
# Notes :
#
# - Run as,
#
# make [-n] -j -f runlevel.mk
# RUNLEVEL=
# JOB=
#
# - $(JO