第二十章、启动流程、模块管理与 Loader
最近升级日期:2009/09/14

大标题的图示Linux 的启动流程分析

启动不是只要按一下电源钮而关机只要关掉电源钮就可以了吗?有何大学问?话是这样没错啦,但是由於 Linux 是一套多人多工的操作系统,你难保你在关机时没有人在线上,如果你关机的时候碰巧一大群人在线上工作, 那会让当时在线上工作的人马上断线的!那不是害死人了!一些数据可是无价之宝哩!

另外 Linux 在运行的时候,虽然你在画面上只会看到黑压压的一片,完全没有任何画面, 但其实他是有很多的程序在背景底下运行的,例如登录档管控程序、前面提到的例行性工作排程等, 当然还有一大堆网络服务,如邮件服务器、WWW 服务器等等。你如果随便关机的话, 是很容易伤害硬盘及数据传输的动作的!所以在 Linux 下关机可是一门大学问喔。


小标题的图示启动流程一览

既然启动是很严肃的一件事,那我们就来了解一下整个启动的过程吧! 好让大家比较容易发现启动过程里面可能会发生问题的地方,以及出现问题后的解决之道! 不过,由於启动的过程中,那个启动管理程序 (Boot Loader) 使用的软件可能不一样,例如目前各大 Linux distributions 的主流为 grub,但早期 Linux 默认是使用 LILO ,台湾地区则很多朋友喜欢使用 spfdisk 。 但无论如何,我们总是得要了解整个 boot loader 的工作情况,才能了解为何进行多重启动的配置时, 老是听人家讲要先安装 Windows 再安装 Linux 的原因~

假设以个人计算机架设的 Linux 主机为例 (先回到第零章计算机概论看看相关的硬件常识喔), 当你按下电源按键后计算机硬件会主动的读取 BIOS 来加载硬件资讯及进行硬件系统的自我测试, 之后系统会主动的去读取第一个可启动的装置 (由 BIOS 配置的) ,此时就可以读入启动管理程序了。

启动管理程序可以指定使用哪个核心文件来启动,并实际加载核心到内存当中解压缩与运行, 此时核心就能够开始在内存内活动,并侦测所有硬件资讯与加载适当的驱动程序来使整部主机开始运行, 等到核心侦测硬件与加载驱动程序完毕后,一个最阳春的操作系统就开始在你的 PC 上面跑了

主机系统开始运行后,此时 Linux 才会呼叫外部程序开始准备软件运行的环境, 并且实际的加载所有系统运行所需要的软件程序哩!最后系统就会开始等待你的登陆与操作啦! 简单来说,系统启动的经过可以汇整成底下的流程的:

  1. 加载 BIOS 的硬件资讯与进行自我测试,并依据配置取得第一个可启动的装置;
  2. 读取并运行第一个启动装置内 MBR 的 boot Loader (亦即是 grub, spfdisk 等程序);
  3. 依据 boot loader 的配置加载 Kernel ,Kernel 会开始侦测硬件与加载驱动程序;
  4. 在硬件驱动成功后,Kernel 会主动呼叫 init 程序,而 init 会取得 run-level 资讯;
  5. init 运行 /etc/rc.d/rc.sysinit 文件来准备软件运行的作业环境 (如网络、时区等);
  6. init 运行 run-level 的各个服务之启动 (script 方式);
  7. init 运行 /etc/rc.d/rc.local 文件;
  8. init 运行终端机模拟程序 mingetty 来启动 login 程序,最后就等待使用者登陆啦;

大概的流程就是上面写的那个样子啦,你会发现 init 这个家伙占的比重非常重! 所以我们才会在第十七章的 pstree 命令中谈到这家伙。 那每一个程序的内容主要是在干嘛呢?底下就分别来谈一谈吧!


小标题的图示BIOS, boot loader 与 kernel 加载

我们在第三章曾经谈过简单的启动流程与 MBR 的功能, 当时为了多重启动而进行的简短的介绍。现在你已经有足够的 Linux 基础了,所以底下让我们来加强说明啦!


  • BIOS, 启动自我测试与 MBR

我们在第零章的计算机概论就曾谈过计算机主机架构, 在个人计算机架构下,你想要启动整部系统首先就得要让系统去加载 BIOS (Basic Input Output System),并透过 BIOS 程序去加载 CMOS 的资讯,并且藉由 CMOS 内的配置值取得主机的各项硬件配置, 例如 CPU 与周边设备的沟通时脉啊、启动装置的搜寻顺序啊、硬盘的大小与类型啊、 系统时间啊、各周边汇流排的是否启动 Plug and Play (PnP, 随插即用装置) 啊、 各周边设备的 I/O 位址啊、以及与 CPU 沟通的 IRQ 岔断等等的资讯。

在取得这些资讯后,BIOS 还会进行启动自我测试 (Power-on Self Test, POST) (注1)。 然后开始运行硬件侦测的初始化,并配置 PnP 装置,之后再定义出可启动的装置顺序, 接下来就会开始进行启动装置的数据读取了 (MBR 相关的任务开始)。

由於我们的系统软件大多放置到硬盘中嘛!所以 BIOS 会指定启动的装置好让我们可以读取磁碟中的操作系统核心文件。 但由於不同的操作系统他的文件系统格式不相同,因此我们必须要以一个启动管理程序来处理核心文件加载 (load) 的问题, 因此这个启动管理程序就被称为 Boot Loader 了。那这个 Boot Loader 程序安装在哪里呢?就在启动装置的第一个磁区 (sector) 内,也就是我们一直谈到的 MBR (Master Boot Record, 主要启动记录区)

那你会不会觉得很奇怪啊?既然核心文件需要 loader 来读取,那每个操作系统的 loader 都不相同, 这样的话 BIOS 又是如何读取 MBR 内的 loader 呢?很有趣的问题吧!其实 BIOS 是透过硬件的 INT 13 中断功能来读取 MBR 的,也就是说,只要 BIOS 能够侦测的到你的磁碟 (不论该磁碟是 SATA 还是 IDE 介面),那他就有办法透过 INT 13 这条通道来读取该磁碟的第一个磁区内的 MBR 啦!(注2) 这样 boot loader 也就能够被运行罗!

Tips:
我们知道每颗硬盘的第一个磁区内含有 446 bytes的 MBR 区域,那么如果我的主机上面有两颗硬盘的话, 系统会去哪颗硬盘的 MBR 读取 boot loader 呢?这个就得要看 BIOS 的配置了。 基本上,我们常常讲的『系统的 MBR』其实指的是 第一个启动装置的 MBR 才对! 所以,改天如果你要将启动管理程序安装到某颗硬盘的 MBR 时, 要特别注意当时系统的『第一个启动装置』是哪个,否则会安装到错误的硬盘上面的 MBR 喔!重要重要!
鸟哥的图示

  • Boot Loader 的功能

刚刚说到 Loader 的最主要功能是要认识操作系统的文件格式并据以加载核心到主内存中去运行。 由於不同操作系统的文件格式不一致,因此每种操作系统都有自己的 boot loader 啦!用自己的 loader 才有办法加载核心文件嘛!那问题就来啦,你应该有听说过多重操作系统吧?也就是在一部主机上面安装多种不同的操作系统。 既然你 (1)必须要使用自己的 loader 才能够加载属於自己的操作系统核心,而 (2)系统的 MBR 只有一个,那你怎么会有办法同时在一部主机上面安装 Windows 与 Linux 呢?

这就得要回到第八章的磁碟文件系统去回忆一下文件系统功能了。 其实每个文件系统 (filesystem, 或者是 partition) 都会保留一块启动磁区 (boot sector) 提供操作系统安装 boot loader , 而通常操作系统默认都会安装一份 loader 到他根目录所在的文件系统的 boot sector 上。如果我们在一部主机上面安装 Windows 与 Linux 后,该 boot sector, boot loader 与 MBR 的相关性会有点像下图:

boot loader 安装在 MBR, boot sector 与操作系统的关系
图 1.2.1、 boot loader 安装在 MBR, boot sector 与操作系统的关系

如上图所示,每个操作系统默认是会安装一套 boot loader 到他自己的文件系统中 (就是每个 filesystem 左下角的方框),而在 Linux 系统安装时,你可以选择将 boot loader 安装到 MBR 去,也可以选择不安装。 如果选择安装到 MBR 的话,那理论上你在 MBR 与 boot sector 都会保有一份 boot loader 程序的。 至於 Windows 安装时,他默认会主动的将 MBR 与 boot sector 都装上一份 boot loader!所以啦, 你会发现安装多重操作系统时,你的 MBR 常常会被不同的操作系统的 boot loader 所覆盖啦! ^_^

我们刚刚提到的两个问题还是没有解决啊!虽然各个操作系统都可以安装一份 boot loader 到他们的 boot sector 中, 这样操作系统可以透过自己的 boot loader 来加载核心了。问题是系统的 MBR 只有一个哩! 你要怎么运行 boot sector 里面的 loader 啊?这个我们得要回忆一下第三章约略提过的 boot loader 的功能了。boot loader 主要的功能如下:

  • 提供菜单:使用者可以选择不同的启动项目,这也是多重启动的重要功能!
  • 加载核心文件:直接指向可启动的程序区段来开始操作系统;
  • 转交其他 loader:将启动管理功能转交给其他 loader 负责。

由於具有菜单功能,因此我们可以选择不同的核心来启动。而由於具有控制权转交的功能,因此我们可以加载其他 boot sector 内的 loader 啦!不过 Windows 的 loader 默认不具有控制权转交的功能,因此你不能使用 Windows 的 loader 来加载 Linux 的 loader 喔!这也是为啥第三章谈到 MBR 与多重启动时,会特别强调先装 Windows 再装 Linux 的缘故。 我们将上述的三个功能以底下的图示来解释你就看的懂了!(与第三章的图示也非常类似啦!)

启动管理程序的菜单功能与控制权转交功能示意图
图 1.2.2、 启动管理程序的菜单功能与控制权转交功能示意图

如上图所示,我的 MBR 使用 Linux 的 grub 这个启动管理程序,并且里面假设已经有了三个菜单, 第一个菜单可以直接指向 Linux 的核心文件并且直接加载核心来启动;第二个菜单可以将启动管理程序控制权交给 Windows 来管理,此时 Windows 的 loader 会接管启动流程,这个时候他就能够启动 windows 了。第三个菜单则是使用 Linux 在 boot sector 内的启动管理程序,此时就会跳出另一个 grub 的菜单啦!了解了吗?

而最终 boot loader 的功能就是『加载 kernel 文件』啦!


  • 加载核心侦测硬件与 initrd 的功能

当我们藉由 boot loader 的管理而开始读取核心文件后,接下来, Linux 就会将核心解压缩到主内存当中, 并且利用核心的功能,开始测试与驱动各个周边装置,包括储存装置、CPU、网络卡、声卡等等。 此时 Linux 核心会以自己的功能重新侦测一次硬件,而不一定会使用 BIOS 侦测到的硬件资讯喔!也就是说,核心此时才开始接管 BIOS 后的工作了。 那么核心文件在哪里啊?一般来说,他会被放置到 /boot 里面,并且取名为 /boot/vmlinuz 才对!

[root@www ~]# ls --format=single-column -F /boot
config-2.6.18-92.el5      <==此版本核心被编译时选择的功能与模块配置档
grub/                     <==就是启动管理程序 grub 相关数据目录
initrd-2.6.18-92.el5.img  <==虚拟文件系统档!
System.map-2.6.18-92.el5  <==核心功能放置到内存位址的对应表
vmlinuz-2.6.18-92.el5     <==就是核心文件啦!最重要者!

从上表我们也可以知道此版本的 Linux 核心为 2.6.18-92.el5 这个版本!为了硬件开发商与其他核心功能开发者的便利, 因此 Linux 核心是可以透过动态加载核心模块的 (就请想成驱动程序即可),这些核心模块就放置在 /lib/modules/ 目录内。 由於模块放置到磁碟根目录内 (要记得 /lib 不可以与 / 分别放在不同的 partition !), 因此在启动的过程中核心必须要挂载根目录,这样才能够读取核心模块提供加载驱动程序的功能。 而且为了担心影响到磁碟内的文件系统,因此启动过程中根目录是以唯读的方式来挂载的喔。

一般来说,非必要的功能且可以编译成为模块的核心功能,目前的 Linux distributions 都会将他编译成为模块。 因此 U盘, SATA, SCSI... 等磁碟装置的驱动程序通常都是以模块的方式来存在的。 现在来思考一种情况,假设你的 linux 是安装在 SATA 磁碟上面的,你可以透过 BIOS 的 INT 13 取得 boot loader 与 kernel 文件来启动,然后 kernel 会开始接管系统并且侦测硬件及尝试挂载根目录来取得额外的驱动程序。

问题是,核心根本不认识 SATA 磁碟,所以需要加载 SATA 磁碟的驱动程序, 否则根本就无法挂载根目录。但是 SATA 的驱动程序在 /lib/modules 内,你根本无法挂载根目录又怎么读取到 /lib/modules/ 内的驱动程序?是吧!非常的两难吧!在这个情况之下,你的 Linux 是无法顺利启动的! 那怎办?没关系,我们可以透过虚拟文件系统来处理这个问题。

虚拟文件系统 (Initial RAM Disk) 一般使用的档名为 /boot/initrd ,这个文件的特色是,他也能够透过 boot loader 来加载到内存中, 然后这个文件会被解压缩并且在内存当中模拟成一个根目录, 且此模拟在内存当中的文件系统能够提供一支可运行的程序,透过该程序来加载启动过程中所最需要的核心模块, 通常这些模块就是 U盘, RAID, LVM, SCSI 等文件系统与磁碟介面的驱动程序啦!等加载完成后, 会帮助核心重新呼叫 /sbin/init 来开始后续的正常启动流程。

BIOS 与 boot loader 及核心加载流程示意图
图 1.2.3、 BIOS 与 boot loader 及核心加载流程示意图

如上图所示,boot loader 可以加载 kernel 与 initrd ,然后在内存中让 initrd 解压缩成为根目录, kernel 就能够藉此加载适当的驱动程序,最终释放虚拟文件系统,并挂载实际的根目录文件系统, 就能够开始后续的正常启动流程。更详细的 initrd 说明,你可以自行使用 man initrd 去查阅看看。 底下让我们来了解一下 CentOS 5.x 的 initrd 文件内容有什么吧! ^_^

# 1. 先将 /boot/initrd 复制到 /tmp/initrd 目录中,等待解压缩:
[root@www ~]# mkdir /tmp/initrd
[root@www ~]# cp /boot/initrd-2.6.18-92.el5.img /tmp/initrd/
[root@www ~]# cd /tmp/initrd
[root@www initrd]# file initrd-2.6.18-92.el5.img
initrd-2.6.18-92.el5.img: gzip compressed data, ...
# 原来是 gzip 的压缩档!因为是 gzip ,所以扩展名给他改成 .gz 吧!

# 2. 将上述的文件解压缩:
[root@www initrd]# mv initrd-2.6.18-92.el5.img initrd-2.6.18-92.el5.gz
[root@www initrd]# gzip -d initrd-2.6.18-92.el5.gz
[root@www initrd]# file initrd-2.6.18-92.el5
initrd-2.6.18-92.el5: ASCII cpio archive (SVR4 with no CRC)
# 搞了老半天,原来还是 cpio 的命令压缩成的文件啊!解压缩看看!

# 3. 用 cpio 解压缩
[root@www initrd]# cpio -ivcdu < initrd-2.6.18-92.el5
[root@www initrd]# ll
drwx------ 2 root root    4096 Apr 10 02:05 bin
drwx------ 3 root root    4096 Apr 10 02:05 dev
drwx------ 2 root root    4096 Apr 10 02:05 etc
-rwx------ 1 root root    1888 Apr 10 02:05 init
-rw------- 1 root root 5408768 Apr 10 02:00 initrd-2.6.18-92.el5
drwx------ 3 root root    4096 Apr 10 02:05 lib
drwx------ 2 root root    4096 Apr 10 02:05 proc
lrwxrwxrwx 1 root root       3 Apr 10 02:05 sbin -> bin
drwx------ 2 root root    4096 Apr 10 02:05 sys
drwx------ 2 root root    4096 Apr 10 02:05 sysroot
# 看!是否很像根目录!尤其也是有 init 这个运行档!务必看一下权限!
# 接下来看看 init 这个文件内有啥咚咚?

# 4. 观察 init 文件内较重要的运行项目
[root@www initrd]# cat init
#!/bin/nash                  <==使用类似 bash 的 shell 来运行
mount -t proc /proc /proc    <==挂载内存的虚拟文件系统
....(中间省略)....
echo Creating initial device nodes
mknod /dev/null c 1 3        <==创建系统所需要的各项装置!
....(中间省略)....
echo "Loading ehci-hcd.ko module"
insmod /lib/ehci-hcd.ko      <==加载各项核心模块,就是驱动程序!
....(中间省略)....
echo Creating root device.
mkrootdev -t ext3 -o defaults,ro hdc2 <==尝试挂载根目录啦!
....(底下省略)....

嘿嘿!透过上述运行档的内容,我们可以知道 initrd 有加载模块并且尝试挂载了虚拟文件系统。 接下来就能够顺利的运行啦!那么是否一定需要 initrd 呢?

例题:
是否没有 initrd 就无法顺利启动?
答:
不见得的!需要 initrd 最重要的原因是,当启动时无法挂载根目录的情况下, 此时就一定需要 initrd ,例如你的根目录在特殊的磁碟介面 (U盘, SATA, SCSI) , 或者是你的文件系统较为特殊 (LVM, RAID) 等等,才会需要 initrd。

如果你的 Linux 是安装在 IDE 介面的磁碟上,并且使用默认的 ext2/ext3 文件系统, 那么不需要 initrd 也能够顺利的启动进入 Linux 的!

在核心完整的加载后,您的主机应该就开始正确的运行了,接下来,就是要开始运行系统的第一支程序: /sbin/init。


小标题的图示第一支程序 init 及配置档 /etc/inittab 与 runlevel

在核心加载完毕、进行完硬件侦测与驱动程序加载后,此时你的主机硬件应该已经准备就绪了 (ready) , 此时核心会主动的呼叫第一支程序,那就是 /sbin/init 罗。这也是为啥第十七章的 pstree 命令介绍时,你会发现 init 的 PID 号码是一号啦。 /sbin/init 最主要的功能就是准备软件运行的环境,包括系统的主机名称、网络配置、语系处理、文件系统格式及其他服务的启动等。 而所有的动作都会透过 init 的配置档,亦即是 /etc/inittab 来规划,而 inittab 内还有一个很重要的配置项目,那就是默认的 runlevel (启动运行等级) 啦!


  • Run level:运行等级有哪些?

那么什么是 run level 呢?他有什么功用啊?其实很简单啦, Linux 就是藉由配置 run level 来规定系统使用不同的服务来启动,让 Linux 的使用环境不同。基本上,依据有无网络与有无 X Window 而将 run level 分为 7 个等级,分别是:

  • 0 - halt (系统直接关机)
  • 1 - single user mode (单人维护模式,用在系统出问题时的维护)
  • 2 - Multi-user, without NFS (类似底下的 runlevel 3,但无 NFS 服务)
  • 3 - Full multi-user mode (完整含有网络功能的纯文字模式)
  • 4 - unused (系统保留功能)
  • 5 - X11 (与 runlevel 3 类似,但加载使用 X Window)
  • 6 - reboot (重新启动)

由於 run level 0, 4, 6 不是关机、重新启动就是系统保留的,所以:『 您当然不能将默认的 run level 配置为这三个值 』, 否则系统就会不断的自动关机或自动重新启动.... 好了,那么我们启动时,到底是如何取得系统的 run level 的?当然是 /etc/inittab 所配置的罗! 那么 /etc/inittab 到底有什么资讯呢?我们先来看看这个文件的内容好了:


  • /etc/inittab 的内容与语法
[root@www ~]# vim /etc/inittab
id:5:initdefault:                 <==默认的 runlevel 配置, 此 runlevel 为 5 

si::sysinit:/etc/rc.d/rc.sysinit  <==准备系统软件运行的环境的脚本运行档

# 7 个不同 run level 的,需要启动的服务的 scripts 放置路径:
l0:0:wait:/etc/rc.d/rc 0    <==runlevel 0 在 /etc/rc.d/rc0.d/
l1:1:wait:/etc/rc.d/rc 1    <==runlevel 1 在 /etc/rc.d/rc1.d/
l2:2:wait:/etc/rc.d/rc 2    <==runlevel 2 在 /etc/rc.d/rc2.d/
l3:3:wait:/etc/rc.d/rc 3    <==runlevel 3 在 /etc/rc.d/rc3.d/
l4:4:wait:/etc/rc.d/rc 4    <==runlevel 4 在 /etc/rc.d/rc4.d/
l5:5:wait:/etc/rc.d/rc 5    <==runlevel 5 在 /etc/rc.d/rc5.d/
l6:6:wait:/etc/rc.d/rc 6    <==runlevel 6 在 /etc/rc.d/rc6.d/

# 是否允许按下 [ctrl]+[alt]+[del] 就重新启动的配置项目:
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# 底下两个配置则是关於不断电系统的 (UPS),一个是没电力时的关机,一个是复电的处理
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"

1:2345:respawn:/sbin/mingetty tty1  <==其实 tty1~tty6 是由底下这六行决定的。
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

x:5:respawn:/etc/X11/prefdm -nodaemon <==X window 则是这行决定的!

让我们解析一下这个文件吧!首先,这个文件的语法是利用冒号 (:) 将配置分隔成为四个栏位,每个栏位的意义与说明如下:

[配置项目]:[run level]:[init 的动作行为]:[命令项目]
  1. 配置项目:最多四个字节,代表 init 的主要工作项目,只是一个简单的代表说明。

  2. run level:该项目在哪些 run level 底下进行的意思。如果是 35 则代表 runlevel 3 与 5 都会运行。

  3. init 的动作项目:主要可以进行的动作项目意义有:
    inittab 配置值意义说明
    initdefault代表默认的 run level 配置值
    sysinit代表系统初始化的动作项目
    ctrlaltdel代表 [ctrl]+[alt]+[del] 三个按键是否可以重新启动的配置
    wait代表后面栏位配置的命令项目必须要运行完毕才能继续底下其他的动作
    respawn代表后面栏位的命令可以无限制的再生 (重新启动)。举例来说, tty1 的 mingetty 产生的可登陆画面, 在你注销而结束后,系统会再开一个新的可登陆画面等待下一个登陆。
    更多的配置项目请参考 man inittab 的说明。

  4. 命令项目:亦即应该可以进行的命令,通常是一些 script 罗。


  • init 的处理流程

事实上 /etc/inittab 的配置也有点类似 shell script 啦,因为该文件内容的配置也是一行一行的从上往下处理的, 因此我们可以知道 CentOS 的 init 依据 inittab 配置的处理流程会是:

  1. 先取得 runlevel 亦即默认运行等级的相关等级 (以鸟哥的测试机为例,为 5 号);
  2. 使用 /etc/rc.d/rc.sysinit 进行系统初始化
  3. 由於 runlevel 是 5 ,因此只进行『l5:5:wait:/etc/rc.d/rc 5』,其他行则略过
  4. 配置好 [ctrl]+[alt]+[del] 这组的组合键功能
  5. 配置不断电系统的 pf, pr 两种机制;
  6. 启动 mingetty 的六个终端机 (tty1 ~ tty6)
  7. 最终以 /etc/X11/perfdm -nodaemon 启动图形介面啦!

现在你可以知道为啥 [ctrl]+[alt]+[del] 可以重新启动而我们默认提供 6 个虚拟终端机 (tty1~tty6) 给你使用了吧!由於整个配置都是依据 /etc/inittab 来决定的,因此如果你想要修改任何细节的话, 可以这样做喔:

  • 如果不想让使用者利用 [crtl]+[alt]+[del] 来重新启动系统,可以将『 ca::ctrlaltdel:/sbin/shutdown -t3 -r now 』加上注解 (#) 来取消该配置

  • 规定启动的默认 run level 是纯文字的 3 号或者是具有图形介面的 5 号 ,可经由 『 id:5:initdefault: 』那个数字来决定! 以鸟哥自己这个文件为例,我是使用默认的图形介面。如果你想要关闭图形介面的话,将该行 5 改成 3 即可。

  • 如果不想要启动六个终端机 (tty1~tty6),那么可以将『 6:2345:respawn:/sbin/mingetty tty6』关闭数个。但务必至少启动一个喔!

所以说,你现在会自行修改登陆时的默认 run level 配置值了吗?够简单的吧? 一般来说,我们默认都是 3 或者是 5 来作为默认的 run level 的。但有时后可能需要进入 run level 1, 也就是单人维护模式的环境当中。这个 run level 1 有点像是 Windows 系统当中的『安全模式』啦, 专门用来处理当系统有问题时的操作环境。此外,当系统发现有问题时,举例来说,不正常关机造成 filesystem 的不一致现象时,系统会主动的进入单人维护模式呢!

好了, init 在取得 run level 之后,接下来要干嘛? 上面 /etc/inittab 文件内容不是有提到 sysinit 吗?准备初始化系统了吧!


小标题的图示init 处理系统初始化流程 (/etc/rc.d/rc.sysinit)

还记得上面提到 /etc/inittab 里头有这一句『 si::sysinit:/etc/rc.d/rc.sysinit 』吧? 这表示:『我开始加载各项系统服务之前,得先做好整个系统环境,我主要利用 /etc/rc.d/rc.sysinit 这个 shell script 来配置好我的系统环境的。』够清楚了吧? 所以,我想要知道到底 CentOS 启动的过程当中帮我进行了什么动作,就得要仔细的分析 /etc/rc.d/rc.sysinit 罗。

Tips:
老实说,这个文件的档名在各不同的 distributions 当中都不相同, 例如 SuSE server 9 就使用 /etc/init.d/boot 与 /etc/init.d/rc 来进行的。 所以,你最好还是自行到 /etc/inittab 去察看一下系统的工作喔! ^_^
鸟哥的图示

如果你使用 vim 去查阅过 /etc/rc.d/rc.sysinit 的话,那么可以发现他主要的工作大抵有这几项:

  1. 取得网络环境与主机类型:
    读取网络配置档 /etc/sysconfig/network ,取得主机名称与默认通讯闸 (gateway) 等网络环境。
  2. 测试与挂载内存装置 /proc 及 U盘 装置 /sys:
    除挂载内存装置 /proc 之外,还会主动侦测系统上是否具有 usb 的装置, 若有则会主动加载 usb 的驱动程序,并且尝试挂载 usb 的文件系统。
  3. 决定是否启动 SELinux :
    我们在
    第十七章谈到的 SELinux 在此时进行一些检测, 并且检测是否需要帮所有的文件重新编写标准的 SELinux 类型 (auto relabel)。
  4. 启动系统的乱数产生器
    乱数产生器可以帮助系统进行一些口令加密演算的功能,在此需要启动两次乱数产生器。
  5. 配置终端机 (console) 字形:
  6. 配置显示於启动过程中的欢迎画面 (text banner);
  7. 配置系统时间 (clock) 与时区配置:需读入 /etc/sysconfig/clock 配置值
  8. 周边设备的侦测与 Plug and Play (PnP) 参数的测试:
    根据核心在启动时侦测的结果 (/proc/sys/kernel/modprobe ) 开始进行 ide / scsi / 网络 / 音效 等周边设备的侦测,以及利用以加载的核心模块进行 PnP 装置的参数测试。
  9. 使用者自订模块的加载
    使用者可以在 /etc/sysconfig/modules/*.modules 加入自订的模块,则此时会被加载到系统当中
  10. 加载核心的相关配置:
    系统会主动去读取 /etc/sysctl.conf 这个文件的配置值,使核心功能成为我们想要的样子。
  11. 配置主机名称与初始化电源管理模块 (ACPI)
  12. 初始化软件磁盘阵列:主要是透过 /etc/mdadm.conf 来配置好的。
  13. 初始化 LVM 的文件系统功能
  14. 以 fsck 检验磁碟文件系统:会进行 filesystem check
  15. 进行磁碟配额 quota 的转换 (非必要):
  16. 重新以可读写模式挂载系统磁碟:
  17. 启动 quota 功能:所以我们不需要自订 quotaon 的动作
  18. 启动系统虚拟乱数产生器 (pseudo-random):
  19. 清除启动过程当中的缓存文件:
  20. 将启动相关资讯加载 /var/log/dmesg 文件中。

在 /etc/rc.d/rc.sysinit 将基本的系统配置数据都写好了,也将系统的数据配置完整! 而如果你想要知道到底启动的过程中发生了什么事情呢?那么就运行『 dmesg 』吧。 另外,基本上,在这个文件当中所进行的很多工作的默认配置档,其实都在 /etc/sysconfig/ 当中呢! 所以,请记得将 /etc/sysconfig/ 内的文件好好的瞧一瞧喔! ^_^

在这个过程当中,比较值得注意的是自订模块的加载!在 CentOS 当中,如果我们想要加载核心模块的话, 可以将整个模块写入到 /etc/sysconfig/modules/*.modules 当中,在该目录下, 只要记得档名最后是以 .modules 结尾即可。 这个过程是非必要的,因为我们目前的默认模块实在已经很够用了,除非是您的主机硬件实在太新了, 非要自己加载新的模块不可,否则,在经过 /etc/rc.d/rc.sysinit 的处理后, 你的主机系统应该是已经跑得很顺畅了啦!就等著你将系统相关的服务与网络服务启动罗!


小标题的图示启动系统服务与相关启动配置档 (/etc/rc.d/rc N & /etc/sysconfig)

加载核心让整个系统准备接受命令来工作,再经过 /etc/rc.d/rc.sysinit 的系统模块与相关硬件资讯的初始化后,你的 CentOS 系统应该已经顺利工作了。 只是,我们还得要启动系统所需要的各项『服务』啊!这样主机才能提供我们相关的网络或者是主机功能嘛! 这个时候,依据我们在 /etc/inittab 里面提到的 run level 配置值,就可以来决定启动的服务项目了。 举例来说,使用 run level 3 当然就不需要启动 X Window 的相关服务罗,您说是吧?

那么各个不同的 run level 服务启动的各个 shell script 放在哪?还记得 /etc/inittab 里面提到的:

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

上面提到的就是各个 run level 要运行的各项脚本放置处啦!主要是透过 /etc/rc.d/rc 这个命令来处理相关任务! 由於鸟哥使用默认的 runlevel 5 ,因此我们主要针对上述特殊字体那行来解释好了: /etc/rc.d/rc 5 的意义是这样的 (建议您自行使用 vim 去观察一下 /etc/rc.d/rc 这个文件,你会更有概念!):

  • 透过外部第一号参数 ($1) 来取得想要运行的脚本目录。亦即由 /etc/rc.d/rc 5 可以取得 /etc/rc5.d/ 这个目录来准备处理相关的脚本程序;
  • 找到 /etc/rc5.d/K??* 开头的文件,并进行『 /etc/rc5.d/K??* stop 』的动作;
  • 找到 /etc/rc5.d/S??* 开头的文件,并进行『 /etc/rc5.d/S??* start 』的动作;

透过上面的说明我们可以知道所有的项目都与 /etc/rc5.d/ 有关,那么我们就来瞧瞧这个目录下有些什么玩意儿吧!

[root@www ~]# ll /etc/rc5.d/
lrwxrwxrwx 1 root root 16 Sep  4  2008 K02dhcdbd -> ../init.d/dhcdbd
....(中间省略)....
lrwxrwxrwx 1 root root 14 Sep  4  2008 K91capi -> ../init.d/capi
lrwxrwxrwx 1 root root 23 Sep  4  2008 S00microcode_ctl -> ../init.d/microcode_ctl
lrwxrwxrwx 1 root root 22 Sep  4  2008 S02lvm2-monitor -> ../init.d/lvm2-monitor
....(中间省略)....
lrwxrwxrwx 1 root root 17 Sep  4  2008 S10network -> ../init.d/network
....(中间省略)....
lrwxrwxrwx 1 root root 11 Sep  4  2008 S99local -> ../rc.local
lrwxrwxrwx 1 root root 16 Sep  4  2008 S99smartd -> ../init.d/smartd
....(底下省略)....

在这个目录下的文件很有趣,主要具有几个特点:

  • 档名全部以 Sxx 或 Kxx ,其中 xx 为数字,且这些数字在文件之间是有相关性的!
  • 全部是连结档,连结到 stand alone 服务启动的目录 /etc/init.d/ 去

我们在第十八章谈过服务的启动主要是以『/etc/init.d/服务档名 {start,stop}』来启动与关闭的,那么透过刚刚 /etc/rc.d/rc 程序的解说,我们可以清楚的了解到了 /etc/rc5.d/[SK]xx 其实就是跑到 /etc/init.d/ 去找到相对应的服务脚本, 然后分别进行 start (Sxx) 或 stop (Kxx) 的动作而已啦!举例来说,以上述的表格内的 K91capi 及 S10network 为例好了, 透过 /etc/rc.d/rc 5 的运行,这两个文件会这样进行:

  • /etc/rc5.d/K91capi stop --> /etc/init.d/capi stop
  • /etc/rc5.d/S10network start --> /etc/init.d/network start

所以说,你有想要启动该 runlevel 时就运行的服务,那么利用 Sxx 并指向 /etc/init.d/ 的特定服务启动脚本后, 该服务就能够在启动时启动啦!就这么简单!问题是,你需要自行处理这个 K, S 开头的连结档吗? 并不需要的,第十八章谈到的 chkconfig 就是在负责处理这个连结档啦!这样有没有跟第十八章的观念串在一起了呢? ^_^

那么为什么 K 与 S 后面要有数字呢?因为各不同的服务其实还是互有关系的。举例来说,如果要启动 WWW 服务,总是得要有网络吧?所以 /etc/init.d/network 就会比较先被启动啦!那么您就会知道在 S 或者是 K 后面接的数字是啥意思了吧?嘿嘿,那就是运行的顺序啦!那么哪个文件被最后运行呢? 看到最后一个被运行的项目是啥?没错,就是 S99local ,亦即是: /etc/rc.d/rc.local 这个文件啦!


小标题的图示使用者自订启动启动程序 (/etc/rc.d/rc.local)

在完成默认 runlevel 指定的各项服务的启动后,如果我还有其他的动作想要完成时,举例来说, 我还想要寄一封 mail 给某个系统管理帐号,通知他,系统刚刚重新启动完毕,那么是否应该要制作一个 shell script 放置在 /etc/init.d/ 里面,然后再以连结方式连结到 /etc/rc5.d/ 里面呢?呵呵!当然不需要!还记得上一小节提到的 /etc/rc.d/rc.local 吧? 这个文件就可以运行您自己想要运行的系统命令了。

也就是说,我有任何想要在启动时就进行的工作时,直接将他写入 /etc/rc.d/rc.local , 那么该工作就会在启动的时候自动被加载喔!而不必等我们登陆系统去启动呢! 是否很方便啊!一般来说,鸟哥就很喜欢把自己制作的 shell script 完整档名写入 /etc/rc.d/rc.local ,如此一来,启动就会将我的 shell script 运行过,真是好棒那!


小标题的图示根据 /etc/inittab 之配置,加载终端机或 X-Window 介面

在完成了系统所有服务的启动后,接下来 Linux 就会启动终端机或者是 X Window 来等待使用者登陆啦! 实际参考的项目是 /etc/inittab 内的这一段:

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
x:5:respawn:/etc/X11/prefdm -nodaemon

这一段代表,在 run level 2, 3, 4, 5 时,都会运行 /sbin/mingetty 这个咚咚, 而且运行六个,这也是为何我们 Linux 会提供『六个纯文字终端机』的配置所在啊! 因为 mingetty 就是在启动终端机的命令说。

要注意的是那个 respawn 的 init 动作项目,他代表『当后面的命令被终止 (terminal) 时, init 会主动的重新启动该项目。』这也是为何我们登陆 tty1 终端机介面后,以 exit 离开后, 系统还是会重新显示等待使用者输入的画面的原因啊!

如果改天您不想要有六个终端机时,可以取消某些终端机介面吗?当然可以啊! 就将上面表格当中的某些项目注解掉即可!例如不想要 tty5 与 tty6 ,就将那两行注解, 则下次重新启动后,您的 Linux 就只剩下『 F1 ~ F4 』有效而已,这样说,可以了解吧!!^_^

至於如果我们使用的是 run level 5 呢?那么除了这六个终端机之外, init 还会运行 /etc/X11/prefdm -nodaemon 那个命令喔!该命令我们会在第二十四章、X Window 再来详谈! 他主要的功能就是在启动 X Window 啦!


小标题的图示启动过程会用到的主要配置档

我们在 /sbin/init 的运行过程中有谈到许多运行脚本,包括 /etc/rc.d/rc.sysinit 以及 /etc/rc.d/rc 等等, 其实这些脚本都会使用到相当多的系统配置档,这些启动过程会用到的配置档则大多放置在 /etc/sysconfig/ 目录下。 同时,由於核心还是需要加载一些驱动程序 (核心模块),此时系统自订的装置与模块对应档 (/etc/modprobe.conf) 就显的挺重要了喔!


  • 关於模块: /etc/modprobe.conf

还记得我们在 /etc/rc.d/rc.sysinit 当中谈到的加载使用者自订模块的地方吗?就是在 /etc/sysconfig/modules/ 目录下啊! 虽然核心提供的默认模块已经很足够我们使用了,但是,某些条件下我们还是得对模块进行一些参数的规划, 此时就得要使用到 /etc/modprobe.conf 罗!举例来说,鸟哥的 CentOS 主机的 modprobe.conf 有点像这样:

[root@www ~]# cat /etc/modprobe.conf
alias eth0 8139too               <==让 eth0 使用 8139too 的模块
alias scsi_hostadapter pata_sis
alias snd-card-0 snd-trident
options snd-card-0 index=0       <==额外指定 snd-card-0 的参数功能
options snd-trident index=0

这个文件大多在指定系统内的硬件所使用的模块啦!这个文件通常系统是可以自行产生的,所以你不必手动去订正他! 不过,如果系统捉到错误的驱动程序,或者是你想要使用升级的驱动程序来对应相关的硬件配备时, 你就得要自行手动的处理一下这个文件了。

以上表的第一行为例,鸟哥使用螃蟹卡 (Realtek 的芯片组) 来作为我的网络卡,那螃蟹卡使用的模块就是 8139too 啦!这样看的懂了吧?当我要启动网络卡时,系统会跑到这个文件来查阅一下,然后加载 8139too 驱动程序来驱动网络卡罗!更多的相关说明,请 man modprobe.conf 喔!


  • /etc/sysconfig/*

不说您也知道,整个启动的过程当中,老是读取的一些服务的相关配置档都是记录在 /etc/sysconfig 目录下的!那么该目录底下有些啥玩意儿?我们找几个重要的文件来谈谈:

  • authconfig
    这个文件主要在规范使用者的身份认证的机制,包括是否使用本机的 /etc/passwd, /etc/shadow 等, 以及 /etc/shadow 口令记录使用何种加密演算法,还有是否使用外部口令服务器提供的帐号验证 (NIS, LDAP) 等。 系统默认使用 MD5 加密演算法,并且不使用外部的身份验证机制;

  • clock
    此文件在配置 Linux 主机的时区,可以使用格林威治时间(GMT),也可以使用台湾的本地时间 (local)。基本上,在 clock 文件内的配置项目『 ZONE 』所参考的时区位於 /usr/share/zoneinfo 目录下的相对路径中。而且要修改时区的话,还得将 /usr/share/zoneinfo/Asia/Taipei 这个文件复制成为 /etc/localtime 才行!

  • i18n
    i18n 在配置一些语系的使用方面,例如最麻烦的文字介面下的日期显示问题! 如果你是以中文安装的,那么默认语系会被选择 zh_TW.UTF8 ,所以在纯文字介面之下, 你的文件日期显示可能就会呈现乱码!这个时候就需要更改一下这里啦!更动这个 i18n 的文件,将里面的 LC_TIME 改成 en 即可!

  • keyboard & mouse
    keyboard 与 mouse 就是在配置键盘与鼠标的形式;

  • network
    network 可以配置是否要启动网络,以及配置主机名称还有通讯闸 (GATEWAY) 这两个重要资讯呢!

  • network-scripts/
    至於 network-scripts 里面的文件,则是主要用在配置网络卡~ 这部份我们在服务器架设篇才会提到!

总而言之一句话,这个目录下的文件很重要的啦!启动过程里面常常会读取到的!


小标题的图示Run level 的切换

在我们完成上面的所有资讯后,其实整个 Linux 主机就已经在等待我们使用者的登陆啦! 但是,相信您应该还是会有一点疑问的地方,那就是:『我该如何切换 run level 呢?』会不会很难啊?不会啦!很简单~但是依据运行的时间而有不同的方式啊!

事实上,与 run level 有关的启动其实是在 /etc/rc.d/rc.sysinit 运行完毕之后。也就是说,其实 run level 的不同仅是 /etc/rc[0-6].d 里面启动的服务不同而已。不过,依据启动是否自动进入不同 run level 的配置,我们可以说:

  1. 要每次启动都运行某个默认的 run level ,则需要修改 /etc/inittab 内的配置项目, 亦即是『 id:5:initdefault: 』里头的数字啊;

  2. 如果仅只是暂时变更系统的 run level 时,则使用 init [0-6] 来进行 run level 的变更。 但下次重新启动时,依旧会是以 /etc/inittab 的配置为准。

假设原本我们是以 run level 5 登陆系统的,但是因为某些因素,想要切换成为 run level 3 时, 该怎么办呢?很简单啊,运行『 init 3 』即可切换。但是 init 3 这个动作到底做了什么呢? 我们不是说了吗?事实上,不同的 run level 只是加载的服务不同罢了, 亦即是 /etc/rc5.d/ 还有 /etc/rc3.d 内的 Sxxname 与 Kxxname 有差异而已。 所以说,当运行 init 3 时,系统会:

  • 先比对 /etc/rc3.d/ 及 /etc/rc5.d 内的 K 与 S 开头的文件;
  • 在新的 runlevel 亦即是 /etc/rc3.d/ 内有多的 K 开头文件,则予以关闭;
  • 在新的 runlevel 亦即是 /etc/rc3.d/ 内有多的 S 开头文件,则予以启动;

也就是说,两个 run level 都存在的服务就不会被关闭啦!如此一来,就很容易切换 run level 了, 而且还不需要重新启动呢!真方便。那我怎么知道目前的 run level 是多少呢? 直接在 bash 当中输入 runlevel 即可啊!

[root@www ~]# runlevel
N 5
# 左边代表前一个 runlevel ,右边代表目前的 runlevel。
# 由於之前并没有切换过 runlevel ,因此前一个 runlevel 不存在 (N)
# 将目前的 runlevel 切换成为 3 (注意, tty7 的数据会消失!)
[root@www ~]# init 3
NIT: Sending processes the TERM signal
Applying Intel CPU microcode update:        [  OK  ]
Starting background readahead:              [  OK  ]
Starting irqbalance:                        [  OK  ]
Starting httpd:                             [  OK  ]
Starting anacron:                           [  OK  ]
# 这代表,新的 runlevel 亦即是 runlevel3 比前一个 runlevel 多出了上述 5 个服务

[root@www ~]# runlevel
5 3
# 看吧!前一个是 runlevel 5 ,目前的是 runlevel 3 啦!

那么你能不能利用 init 来进行关机与重新启动呢?可以的啦!利用『 init 0 』就能够关机, 而『 init 6 』就能够重新启动!为什么?往前翻一下 runlevel 的定义即可了解吧!


 
     
http://linux.vbird.org is designed by VBird during 2001-2011. ksu.edu 

本网页主要以Firefox配合解析度 1024x768 作为设计依据     鸟哥自由软件整合应用研究室