第20章 启动流程、模块管理与Loader

系统启动其实是一项非常复杂的程序,因为内核得先检测硬件并加载适当的驱动程序后,接下来则必须要调用程序来准备好系统运行的环境,以让用户能够顺利操作整台主机系统。如果你能够理解启动的原理,那么将有助于你在系统出问题时能够快速修复系统。而且还能够顺利配置多重操作系统的多重引导问题。为了多重引导的问题,你就不能不学学grub这个 Linux 下面优秀的引导装载程序(boot loader)。而在系统运行期间,你也得要学会管理内核模块呢!

20.1 Linux 的启动流程分析

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

另外Linux在执行的时候,虽然你在界面上只会看到黑黑的一片,完全没有任何界面,但其实它是有很多的进程在后台下面执行的,例如日志文件管理程序、前面提到的例行性工作调度等,当然还有一大堆网络服务,如邮件服务器、WWW 服务器等。你如果随便关机的话,是很容易伤害硬盘及数据传输的操作。所以在Linux下关机可是一门大学问。

20.1.1 启动流程一览

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

假设以个人计算机架设的Linux主机为例(先回到第0章计算机概论看看相关的硬件常识),当你按下电源按键后计算机硬件会主动读取 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这个家伙占的比重非常重。所以我们才会在第17章的pstree命令中谈到这家伙。那每一个程序的内容主要是在干嘛呢?下面就分别来谈一谈吧!

20.1.2 BIOS,boot loader 与kernel 加载

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

BIOS,开机自我测试与MBR

我们在第0章的计算机概论就曾谈过计算机主机架构,在个人计算机架构下,你想要启动整个系统首先就得要让系统去加载 BIOS(Basic Input Output System),并通过 BIOS 程序去加载CMOS的信息,并且通过CMOS内的设置值取得主机的各项硬件配置,例如CPU与接口设备的通信频率、启动设备的查找顺序、硬盘的大小与类型、系统时间、各周边总线是否启动 Plugand Play(PnP,即插即用设备)、各接口设备的 I/O 地址以及与 CPU 通信的 IRQ中断等的信息。

在取得这些信息后,BIOS 还会进行开机自检(Power-on Self Test, POST) [1]。然后开始执行硬件检测的初始化,并配置 PnP 设备,之后再定义出可启动的设备顺序,接下来就会开始进行启动设备的数据读取了(MBR相关的任务开始)。

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

那你会不会觉得很奇怪啊?既然内核文件需要loader来读取,那每个操作系统的loader都不相同,这样的话BIOS又是如何读取MBR内的loader呢?很有趣的问题吧!其实BIOS是通过硬件的 INT 13 中断功能来读取 MBR 的,也就是说,只要 BIOS 能够检测得到你的磁盘(不论该磁盘是SATA还是IDE接口),那它就有办法通过INT13这条信道来读取该磁盘的第一个扇区内的 MBR [2]。这样 boot loader 也就能够被执行。

我们知道每块硬盘的第一个扇区内含有446 B的MBR区域,那么如果我的主机上面有两块硬盘的话,系统会去哪块硬盘的MBR读取boot loader呢?这个就得要看BIOS的设置了。基本上,我们经常讲的“系统的MBR”其实指的是第一个启动设备的MBR才对!所以,改天如果你要将引导装载程序安装到某块硬盘的MBR时,要特别注意当时系统的第一个启动设备是哪个,否则会安装到错误的硬盘上面的MBR。尤其重要!

Boot Loader 的功能

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

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

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

figure_0592_0334

图20-1 boot sector与操作系统的关系

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

提供菜单:用户可以选择不同的启动选项,这也是多重引导的重要功能!

加载内核文件:直接指向可启动的程序区段来开始操作系统。

转交其他loader:将引导装载功能转交给其他loader负责。

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

figure_0592_0335

图20-2 引导装载程序的菜单功能与控制权转交功能示意图

如图20-1所示,我的MBR使用Linux的grub这个引导装载程序,并且里面假设已经有了3个菜单,第一个菜单可以直接指向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 不可以与/分别放在不同的分区),因此在启动的过程中内核必须要挂载根目录,这样才能够读取内核模块提供加载驱动程序的功能,而且为了担心影响到磁盘内的文件系统,因此启动过程中根目录是以只读的方式来挂载。

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

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

虚拟文件系统(InitialRAM Disk)一般使用的文件名为/boot/initrd,这个文件的特色是,它也能够通过 boot loader 来加载到内存中,然后这个文件会被解压缩并且在内存当中仿真成一个根目录,且此仿真在内存当中的文件系统能够提供一个可执行的程序,通过该程序来加载启动过程中所最需要的内核模块,通常这些模块就是USB, RAID, LVM, SCSI 等文件系统与磁盘接口的驱动程序。等载入完成后,会帮助内核重新调用 /sbin/init 来开始后续的正常启动流程。

figure_0593_0336

图20-3 BIOS 与boot loader及内核加载流程示意图

如图 20-3 所示,Boot Loader 可以加载 kernel 与 initrd,然后在内存中让 initrd 解压缩成为根目录,kernel就能够借此加载适当的驱动程序,最终释放虚拟文件系统,并挂载实际的根目录文件系统,就能够开始后续的正常启动流程。更详细的 initrd 说明,你可以自行使用maninitrd 去查阅看看。下面让我们来了解一下 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呢?

figure_0594_0337

是否没有initrd就无法顺利启动?

答:不见得,需要 initrd 最重要的原因是,当启动时无法挂载根目录的情况下,此时就一定需要initrd,例如你的根目录在特殊的磁盘接口(USB, SATA, SCSI)中,或者是你的文件系统较为特殊(LVM,RAID等),才会需要initrd。

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

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

20.1.3 第一个进程init 及配置文件/etc/inittab与 runlevel

在内核加载完毕进行完硬件检测与驱动程序加载后,此时你的主机硬件应该已经准备就绪了(ready),此时内核会主动调用第一个进程,那就是/sbin/init。这也是为什么第17章介绍pstree命令时,你会发现init的PID号码是1号。/sbin/init最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设置、语系处理、文件系统格式及其他服务的启动等。而所有的操作都会通过 init 的配置文件,即是/etc/inittab 来规划,而 inittab 内还有一个很重要的设置选项,那就是默认的 run level(启动执行等级)。

run level:执行等级

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

0 - halt (系统直接关机);

1 - single user mode(单用户维护模式,用在系统出问题时的维护);

2 - Multi-user, without NFS(类似下面的runlevel3,但无NFS 服务);

3 - Full multi-user mode(完整含有网络功能的纯文本模式);

4 - unused(系统保留功能);

5 - X11(与runlevel 3 类似,但加载使用X Window);

6 - reboot(重新启动)。

由于 runlevel 0/4/6 不是关机、重新启动就是系统保留的,所以你当然不能将默认的run level设置为这三个值”,否则系统就会不断自动关机或自动重新启动。好了,那么我们启动时,到底是如何取得系统的 run level 的?当然是/etc/inittab 所设置的。那么/etc/inittab 到底有什么信息呢?我们先来看看这个文件的内容好了:

/etc/inittab的内容与语法

[root@www ~]# vim /etc/inittab

id:5:initdefault:    <==默认的 run level 设置, 此 run level 为 5

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

7 个不同 run level 的,需要启动的服务的 script放置路径:

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的操作行为:主要可以进行的操作选项意义如表20-1所示。

表20-1

figure_0596_0338

更多的设置项目请参考 man inittab 的说明。

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

init的处理流程

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

1.先取得runlevel即默认执行等级的相关等级(以鸟哥的测试机为例,为5号);

2.使用/etc/rc.d/rc.sysinit进行系统初始化;

3.由于runlevel是5,因此只进行“5:5:wait:/etc/rc.d/rc5”,其他行则略过;

4.设置好[ctrl]+[alt]+[del]这组的组合键功能;

5.设置不断电系统的 pf, pr 两种机制;

6.启动 mingetty 的 6 个终端机(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即可。

如果不想要启动 6 个终端机(tty1~tty6),那么可以将“6:2345:respawn:/ sbin/mingetty tty6”关闭多个。但务必至少激活一个。

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

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

20.1.4 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。

老实说,这个文件的文件名在不同的 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及USB设备/sys:

除挂载内存设备/proc之外,还会主动检测系统上是否具有usb的设备,若有则会主动加载usb的驱动程序,并且尝试挂载usb的文件系统。

3.决定是否启动SELinux:

我们在第17章谈到的SELinux在此时进行一些检测,并且检测是否需要帮所有的文件重新编写标准的SELinux类型(autorelabel)。

4.启动系统的随机数生成器

随机数生成器可以帮助系统进行一些密码加密演算的功能,在此需要启动两次随机数生成器。

5.设置终端机(console)字体。

6.设置显示于启动过程中的欢迎界面(textbanner)。

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的处理后,你的主机系统应该是已经跑得很顺畅了,就等着你将系统相关的服务与网络服务启动。

20.1.5 启动系统服务与相关启动配置文件(/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 这个命令来处理相关任务。由于鸟哥使用默认的 run level 5,因此我们主要针对上述特殊字体那行来解释好了:/etc/rc.d/rc5的意义是这样的:(建议你自行使用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/去。

我们在第18章谈过服务的启动主要是以/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/rc5的执行,这两个文件会这样进行:

/etc/rc5.d/K91capistop—>/etc/init.d/capistop

/etc/rc5.d/S10networkstart—>/etc/init.d/networkstart

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

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

20.1.6 用户自定义开机启动程序(/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,真是好棒啊!

20.1.7 根据/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,而且执行 6 个,这也是为何我们 Linux会提供6个纯文本终端机的设置所在,因为mingetty就是在启动终端机的命令。

要注意的是那个respawn的init操作选项,它代表当后面的命令被终止(terminal)时,init会主动重新启动该选项。这也是为何我们登录tty1终端机接口后,以exit离开后,系统还是会重新显示等待用户输入的界面的原因。

如果改天你不想要有6个终端机时,可以取消某些终端机接口吗?当然可以。就将上面的某些项目批注掉即可!例如不想要tty5与tty6,就将那两行批注,则下次重新启动后,你的Linux就只剩下F1~F4有效而已,这样说,可以了解吧!

至于如果我们使用的是 run level 5 呢?那么除了这 6 个终端机之外,init 还会执行/etc/X11/prefdm- nodaemon 那个命令。该命令我们会在第 24 章、X Window 再来详谈!它主要的功能就是启动X Window。

20.1.8 启动过程会用到的主要配置文件

我们在/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/Shanghai这个文件复制成为/etc/localtime才行!

i18n

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

keyboard & mouse

keyboard与mouse就是设置键盘与鼠标的形式。

network

network 可以设置是否要启动网络,以及设置主机名还有网关(GATEWAY)这两个重要信息呢!

network-scripts/

至于network-scripts里面的文件,则是主要用于设置网卡。这部分我们在服务器架设篇才会提到!

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

20.1.9 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 时,该怎么办呢?很简单,执行“init3”即可切换。但是init3这个操作到底做了什么呢?我们不是说了吗?事实上,不同的 run level 只是加载的服务不同罢了,即是/etc/rc5.d/还有/etc/rc3.d 内的 Sxxname 与Kxxname有差异而已。所以说,当执行init3时,系统会:

先比较/etc/rc3.d/及/etc/rc5.d内的K与S开头的文件;

在新的 run level 即是/etc/rc3.d/内有多的 K 开头文件,则予以关闭;

在新的 run level 即是/etc/rc3.d/内有多的 S 开头文件,则予以启动。

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

[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 (即是 runlevel 3)比前一个 runlevel 多出了上述 5 个服务

[root@www ~]# runlevel

5 3

看吧!前一个是 runlevel 5 ,目前的是 runlevel 3 。

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

20.2 内核与内核模块

谈完了整个启动的流程,你应该会知道,在整个启动的过程当中,是否能够成功驱动我们主机的硬件配备是内核(kernel)的工作。而内核一般都是压缩文件,因此在使用内核之前,就得要将它解压缩后才能加载内存当中。

另外,为了应付日新月异的硬件,目前的内核都是具有可读取模块化驱动程序的功能,即所谓的“modules”(模块化)的功能。所谓的模块化可以将它想成是一个“插件”,该插件可能由硬件开发厂商提供,也有可能我们的内核本来就支持。不过,较新的硬件通常都需要硬件开发商提供驱动程序模块。

那么内核与内核模块放在哪?

内核:/boot/vmlinuz或/boot/vmlinuz-version;

内核解压缩所需 RAMDisk:/boot/initrd(/boot/initrd-version);

内核模块:/lib/modules/version/kernel或/lib/modules/$(uname-r)/kernel;

内核源码:/usr/src/linux或/usr/src/kernels(要安装才会有!默认不安装)。

如果该内核被顺利加载到系统当中了,那么就会有几个信息记录下来:

内核版本:/proc/version;

系统内核功能:/proc/sys/kernel。

问题来啦,如果我有个新的硬件,偏偏我的操作系统不支持,该怎么办?很简单啊!

重新编译内核,并加入最新的硬件驱动程序源码;

将该硬件的驱动程序编译成为模块,在启动时加载该模块。

上面第一点还很好理解,反正就是重新编译内核就是了。不过,内核编译很不容易啊!我们会在后续章节简单介绍内核编译的整个程序。比较有趣的则是将该硬件的驱动程序编译成为模块。关于编译的方法,可以参考后续的第22章源码与tarball的介绍。我们这个章节仅是说明一下,如果想要加载一个已经存在的模块时,该如何是好?

20.2.1 内核模块与依赖性

既然要处理内核模块,自然就得要了解了解我们内核提供的模块之间的相关性。基本上,内核模块的放置处是在/lib/modules/$(uname -r)/kernel 当中,里面主要还分成几个目录:

arch :与硬件平台有关的选项,例如 CPU 的等级等;

crypto :内核所支持的加密的技术,例如 md5 或者是 des 等;

drivers :一些硬件的驱动程序,例如显卡、网卡、PCI 相关硬件等;

fs :内核所支持的文件系统,例如 vfat, reiserfs, nfs 等;

lib :一些函数库;

net :与网络有关的各项协议数据,还有防火墙模块 (net/ipv4/netfilter/*) 等;

sound :与音效有关的各项模块;

如果要我们一个一个去检查这些模块的主要信息,然后定义出它们的依赖性,我们可能会疯掉。所以说,我们的 Linux 当然会提供一些模块依赖性的解决方案。对啦!那就是检查/lib/modules/$(uname -r)/modules.dep 这个文件。它记录了在内核支持的模块的各项依赖性。

那么这个文件如何创建呢?挺简单!利用depmod这个命令就可以达到创建该文件的需求了!

[root@www ~]# depmod [-Ane]

参数:

-A :不加任何参数时,depmod 会主动去分析目前内核的模块,并且重新写入

/lib/modules/$(uname -r)/modules.dep 当中。若加入 -A 参数时,则 depmod

会查找比 modules.dep 内还要新的模块,如果真找到新模块,才会更新。

-n :不写入 modules.dep ,而是将结果输出到屏幕上(standard out);

-e :显示出目前已加载的不可执行的模块名称

范例一:若我做好一个网卡驱动程序,文件名为 a.ko,该如何更新内核依赖性?

[root@www ~]# cp a.ko /lib/modules/$(uname -r)/kernel/drivers/net

[root@www ~]# depmod

以上面的范例一为例,我们的 Linuxkernel 2.6.x 版本的内核模块扩展名一定是.ko 结尾的,当你使用 depmod 之后,该程序会跑到模块标准放置目录/lib/modules/$(uname -r)/kernel,并依据相关目录的定义将全部的模块找出来分析,最终才将分析的结果写入modules.dep文件中的。这个文件很重要,因为它会影响到本章稍后会介绍的modprobe命令的应用。

20.2.2 内核模块的查看

那你到底知不知道目前内核加载了多少的模块呢?很简单,利用lsmod即可!

[root@www ~]# lsmod

Module    Size Used by

autofs4   24517 2

hidp    23105 2

….(中间省略)….

8139too   28737 0

8139cp    26305 0

mii    9409 2 8139too,8139cp <==mii 还被 8139cp, 8139too 使用

….(中间省略)….

uhci_hcd   25421 0 <==下面三个是 USB 相关的模块!

ohci_hcd   23261 0

ehci_hcd   33357 0

使用lsmod之后,系统会显示出目前已经存在于内核当中的模块,显示的内容包括:

模块名称(Module);

模块的大小(size);

此模块是否被其他模块所使用(Used by)。

也就是说,模块其实真的有依赖性。上文中,mii这个模块会被8139too所使用。简单说,就是当你要加载8139too时,需要先加载mii这个模块才可以顺利加载8139too。那么除了显示出目前的模块外,我还可以查阅每个模块的信息吗?举例来说,我们知道8139too是网卡的驱动程序,那么mii是什么?就用modinfo来查看吧!

[root@www ~]# modinfo [-adln] [module_name|filename]

参数:

-a :仅列出作者名称;

-d :仅列出该 modules 的说明 (description);

-l :仅列出授权 (license);

-n :仅列出该模块的详细路径。

范例一:从上面的lsmod例子中,列出 mii 这个模块的相关信息:

[root@www ~]# modinfo mii

filename:  /lib/modules/2.6.18-92.el5/kernel/drivers/net/mii.ko

license:  GPL

description: MII hardware support library

author:  Jeff Garzik <jgarzik@pobox.com>

srcversion: 16DCEDEE4B5629C222C352D

depends:

vermagic:  2.6.18-92.el5 SMP mod_unload 686 REGPARM 4KSTACKS gcc-4.1

可以看到这个模块的来源,以及该模块的简易说明(是硬件支持库)!

范例二:我有一个模块名称为 a.ko ,请问该模块的信息是什么?

[root@www ~]# modinfo a.ko

….(省略)….

事实上,这个 modinfo 除了可以查阅内核内的模块之外,还可以检查某个模块文件,因此,如果你想要知道某个文件代表的意义,利用modinfo加上完整文件名看看就知道是什么了。

20.2.3 内核模块的加载与删除

好了,如果我想要自行手动加载模块,又该如何是好?有很多方法,最简单而且建议的是使用modprobe这个命令来加载模块,这是因为modprobe会主动去查找modules.dep的内容,先克服了模块的依赖性后,才决定需要加载的模块有哪些,很方便。至于 insmod 则完全由用户自行加载一个完整文件名的模块,并不会主动分析模块依赖性。

[root@www ~]# insmod [/full/path/module_name] [parameters]

范例一:请尝试载入 cifs.ko 这个文件系统模块

[root@www ~]# insmod /lib/modules/$(uname -r)/kernel/fs/cifs/cifs.ko

[root@www ~]# lsmod | grep cifs

cifs    212789 0

它立刻就将该模块加载,但是 insmod 后面接的模块必须要是完整的“文件名”才行!那如何删除这个模块呢?

[root@www~]# rmmod [-fw] module_name

参数:

-f :强制将该模块删除掉,不论是否正被使用;

-w :若该模块正被使用,则 rmmod 会等待该模块被使用完毕后才删除它!

范例一:将刚才加载的 cifs 模块删除!

[root@www ~]# rmmod cifs

范例二:请加载 vfat 这个文件系统模块

[root@www ~]# insmod /lib/modules/$(uname -r)/kernel/fs/vfat/vfat.ko

insmod: error inserting '/lib/modules/2.6.18-92.el5/kernel/fs/vfat/vfat.ko':

-1 Unknown symbol in module

无法加载 vfat 这个模块。

使用insmod与rmmod的问题就是,你必须要自行找到模块的完整文件名才行,而且如同上述范例二的结果,万一模块有依赖属性的问题时,你将无法直接加载或删除该模块呢!所以近年来我们都建议直接使用modprobe来处理模块加载的问题,这个命令的用法是:

[root@www~]# modprobe [-lcfr] module_name

参数:

-c :列出目前系统所有的模块!(更详细的代号对应表);

-l :列出目前在 /lib/modules/uname -r/kernel 当中的所有模块完整文件名;

-f :强制加载该模块;

-r :类似 rmmod ,就是删除某个模块。

范例一:加载 cifs 模块

[root@www ~]# modprobe cifs

很方便吧!不需要知道完整的模块文件名,这是因为该完整文件名已经记录到

/lib/modules/uname -r/modules.dep 当中的缘故。如果要删除的话,使用以下命令:

[root@www ~]# modprobe -r cifs

使用modprobe真的是要比insmod方便很多!因为它是直接去查找modules.dep的记录,所以,可以克服模块的依赖性问题,而且还不需要知道该模块的详细路径呢!好方便!

figure_0605_0339

尝试使用modprobe加载vfat这个模块,并且查看该模块的相关模块是哪个。

箰:我们使用modprobe来加载,再以lsmod与grep来查看、选取关键字看看:

[root@www ~]# modprobe vfat

[root@www ~]# lsmod | grep vfat

vfat    15809 0

fat    51165 1 vfat <==原来就是 fat 这个模块啊!

[root@www ~]# modprobe -r vfat <==测试完删除此模块

20.2.4 内核模块的额外参数设置:/etc/modprobe.conf

这个文件我们之前已经谈过了,这里只是再强调一下而已,如果你想要修改某些模块的额外参数设置,就在这个文件内设置吧!我们假设一个案例好了,假设我的网卡eth0是使用ne,但是eth1同样也使用ne,为了避免同一个模块会导致网卡的错乱,因此,我可以先找到eth0与eth1的I/O与IRQ,假设:

eth0:I/O(0x300)且IRQ=5

eth1:I/O(0x320)且IRQ=7

则:

[root@www~]# vi /etc/modprobe.conf

alias eth0 ne

alias eth1 ne

options eth0 io=0x300 irq=5

options eth1 io=0x320 irq=7

如此一来,我的Linux就不会捕获错网卡的对应了,因为被我强制指定某个I/O了。

20.3 Boot Loader: Grub

在看完了前面的整个启动流程以及内核模块的整理之后,你应该会发现到一件事情,那就是bootloader 是载入内核的重要工具。没有 boot loader 的话,那么 kernel 根本就没有办法被系统加载的呢!所以,下面我们会先谈一谈 boot loader 的功能,然后再讲一讲现阶段 Linux里头最主流的 grub这个 boot loader 吧!

20.3.1 boot loader 的两个 stage

我们在第一小节启动流程的地方曾经讲过,在 BIOS 读完信息后,接下来就是会到第一个启动设备的 MBR 去读取 boot loader 了。这个 boot loader 可以具有菜单功能、直接加载内核文件以及控制权移交的功能等,系统必须要有 loader 才有办法加载该操作系统的内核就是了。但是我们都知道, MBR 是整个硬盘的第一个sector 内的一个块,充其量整个大小也才446bytes 而已。我们的 loader 功能这么强,光是程序代码与设置数据不可能只占不到446bytes的容量吧?那如何安装?

为了解决这个问题,所以Linux将boot loader的程序代码执行与设置值加载分成两个阶段(stage)来执行:

Stage1:执行boot loader 主程序

第一阶段为执行 boot loader 的主程序,这个主程序必须要被安装在启动区,即是 MBR 或者是 boot sector。但如前所述,因为 MBR 实在太小了,所以,MBR 或 boot sector 通常仅安装 boot loader 的最小主程序,并没有安装 loader 的相关配置文件。

Stage2:主程序加载配置文件

第二阶段为通过 boot loader 加载所有配置文件与相关的环境参数文件(包括文件系统定义与主要配置文件menu.lst),一般来说,配置文件都在/boot下面。

那么这些配置文件是放在哪里啊?这些与grub有关的文件都放置到/boot/grub中,那我们就来看看有哪些文件吧!

[root@www ~]# ls -l /boot/grub

-rw-r—r— device.map   <==grub 的设备对应文件(下面会谈到)

-rw-r—r— e2fs_stage1_5  <==ext2/ext3 文件系统的定义文件

-rw-r—r— fat_stage1_5   <==FAT 文件系统的定义文件

-rw-r—r— ffs_stage1_5   <==FFS 文件系统的定义文件

-rw———- grub.conf   <==grub 在 Red Hat中的配置文件

-rw-r—r— iso9660_stage1_5  <==光驱文件系统定义文件

-rw-r—r— jfs_stage1_5   <==jfs 文件系统定义文件

lrwxrwxrwx menu.lst -> ./grub.conf <==其实 menu.lst 才是配置文件!

-rw-r—r— minix_stage1_5  <==minix 文件系统定义文件

-rw-r—r— reiserfs_stage1_5  <==reiserfs 文件系统定义文件

-rw-r—r— splash.xpm.gz  <==启动时在 grub 下面的后台图示

-rw-r—r— stage1    <==stage 1 的相关说明

-rw-r—r— stage2    <==stage 2 的相关说明

-rw-r—r— ufs2_stage1_5  <==UFS 的文件系统定义文件

-rw-r—r— vstafs_stage1_5  <==vstafs 文件系统定义文件

-rw-r—r— xfs_stage1_5   <==xfs 文件系统定义文件

从上面的说明你可以知道/boot/grub/目录下最重要的就是配置文件(menu.lst)以及各种文件系统的定义!我们的loader读取了这种文件系统定义数据后,就能够认识文件系统并读取该文件系统内的内核文件。至于 grub 的配置文件名,其实应该是 menu.lst 的,只是在 Red Hat 里面被定义成为/boot/grub.conf 而已。鸟哥建议你还是记忆 menu.lst 比较好。

所以从上面的文件来看,grub认识的文件系统真的非常多。正因为如此,所以grub才会替代Lilo 这个老牌的 boot loader。好了,接下来就来瞧瞧配置文件内有什么设置值吧!

20.3.2 grub的配置文件/boot/grub/menu.lst 与菜单类型

grub是目前使用最广泛的Linux引导装载程序,旧的Lilo这个引导装载程序现在已经很少见了,所以本章才会将Lilo的介绍舍弃。grub的优点挺多的,包括:

认识与支持较多的文件系统,并且可以使用grub的主程序直接在文件系统中查找内核文件名;

启动的时候,可以自行编辑与修改启动设置选项,类似bash的命令模式;

可以动态查找配置文件,而不需要在修改配置文件后重新安装 grub ,即我们只要修改完/boot/grub/menu.lst里头的设置后,下次启动就生效了!

上面第三点其实就是 Stage 1,Stage2 分别安装在 MBR(主程序)与文件系统当中(配置文件与定义文件)的原因。好了,接下来,让我们好好了解一下grub的配置文件:/boot/grub/menu.lst,要注意,那个lst是LST的小写,不要搞错。

硬盘与分区在grub中的代号

安装在 MBR 的 grub 主程序最重要的任务之一就是从磁盘当中加载内核文件,以让内核能够顺利驱动整个系统的硬件。所以,grub必须要认识硬盘才行啊!那么grub到底是如何认识硬盘的呢?grub对硬盘的代号设置与传统的Linux磁盘代号可完全是不同的!grub对硬盘的识别使用的是如下的代号:

(hd0,0)

够神了吧?跟/dev/hda1不相同。怎么办啊?其实只要注意几个东西即可,那就是:

硬盘代号以小括号( )括起来;

硬盘以hd表示,后面会接一组数字;

以“查找顺序”作为硬盘的编号,而不是依照硬盘扁平电缆的排序!(这个重要!)

第一个查找到的硬盘为0号,第二个为1号,以此类推;

每块硬盘的第一个分区代号为0,依序类推。

所以说,第一块查找到的硬盘代号为(hd0),而该块硬盘的第一号分区为(hd0,0),这样说了解了吧?反正你要记得,在grub里面,它开始的数字是0而不是1就是了!

在较旧的主板上面,通常第一块硬盘会插在 IDE 1 的 master 上,就会是 /dev/hda,所以经常我们可能会误会 /dev/hda 就是 (hd0),其实不是。要看你的 BIOS 设置值才行!有的主板 BIOS 可以调整启动的硬盘查找顺序,那么就要注意了,因为 grub 的硬盘代号可能会跟着改变。

所以说,整个硬盘代号如表20-2所示。

表20-2

figure_0607_0340

这样应该比较好看出来了吧?第一块硬盘的MBR安装处的硬盘代号就是(hd0),而第一块硬盘的第一个分区的 boot sector 代号就是(hd0,0),第一块硬盘的第一个逻辑分区的 boot sector 代号为“ (hd0,4) ”。

figure_0607_0341

假设你的系统仅有一块 SATA 硬盘,请说明该硬盘的第一个逻辑分区在 Linux 与 grub 当中的文件名与代号。

答:因为是 SATA 磁盘,加上使用逻辑分区,因此 Linux 当中的文件名为/dev/sda5 才对(1~4保留给primary与extended使用)。至于grub当中的磁盘代号则由于仅有一块磁盘,因此代号会是(hd0,4)才对。

/boot/grub/menu.lst配置文件

了解了grub当中最麻烦的硬盘代号后,接下来,我们就可以瞧一瞧配置文件的内容了。先看一下鸟哥的CentOS内的/boot/grub/menu.lst好了:

[root@www ~]# vim /boot/grub/menu.lst

default=0 <==默认启动选项,使用第 1 个启动菜单 (title)

timeout=5 <==若 5 秒内未动键盘,使用默认菜单启动

splashimage=(hd0,0)/grub/splash.xpm.gz <==后台图示所在的文件

hiddenmenu <==读秒期间是否显示出完整的菜单界面(默认隐藏)

title CentOS (2.6.18-92.el5) <==第一个菜单的内容

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet

initrd /initrd-2.6.18-92.el5.img

title以前的四行都是属于grub的整体设置,包括默认的等待时间与默认的启动选项,还有显示的界面特性等。至于 title 后面才是指定启动的内核文件或者是 boot loader 控制权。在整体设置方面的选项主要常见的有:

default=0

这个必须要与title 作为对照,在配置文件里面有几个title,启动的时候就会有几个菜单可以选择。由于grub起始号码为0号,因此default=0代表使用第一个title选项来启动的意思。default的意思是如果在读秒时间结束前都没有按键,grub默认使用此title选项(在此为0号)来启动。

timeout = 5

启动时会进行读秒,如果在5秒钟内没有按下任何按键,就会使用上面提到的default后面接的那个title选项来启动的意思。如果你觉得5秒太短,那可以将这个数值调大(例如30秒)即可。此外,如果timeout=0代表直接使用default值进行启动而不读秒,timeout=-1则代表直接进入菜单不读秒了!

splashimage=(hd0,0)/grub/splash.xpm.gz

有没有发现你的CentOS在启动的时候后台不是黑白而是有色彩变化的呢?那就是这个文件提供的后台图示 [3]。不过这个文件的实际路径写法怎么会是这样啊?很简单!上述代表的是(hd0,0)这个分区内的最顶层目录中下面的grub/splash.xpm.gz那个文件的意思。由于鸟哥将/boot这个目录独立成为/dev/hda1,因此这边就会写成在/dev/hda1里面的grub/splash.xpm.gz的意思。想一想,如果你的/boot目录并没有独立成为一个分区,这里会写成如何?

hiddenmenu

这个说的是,启动时是否要显示菜单?目前 CentOS 默认是不要显示菜单,如果你想要显示菜单,那就将这个设置值批注掉!

整体设置的地方大概是这样,而下面那个title则是显示启动的设置项目。如同前一小节提到的,启动时可以选择直接指定内核文件启动或将boot loader 控制权转移到下个loader(此过程称为 chain-loader)。每个 title 后面接的是该启动项目名称的显示,即在菜单出现时菜单上面的名称而已。那么这两种方式的设置有什么不同呢?

1.直接指定内核启动

既然要指定内核启动,所以当然要找到内核文件。此外,有可能还需要用到initrd的RAMDisk配置文件。但是如前面所说的,尚未启动完成,所以我们必须要以grub的硬盘识别方式找出完整的kernel与initrd文件名才行。因此,我们可能需要有下面的方式来设置才行!

  1. 先指定内核文件放置的分区,再读取文件 (目录树)

最后才加入文件的实际文件名与路径 (kernel 与 initrd);

鸟哥的 /boot 为 /dev/hda1 ,因此内核文件的设置则成为:

root (hd0,0)  <==代表内核文件放在哪个分区当中

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet

initrd /initrd-2.6.18-92.el5.img

上面的 root, kernel,initrd 后面接的参数的意义说明如下:

root:代表的是内核文件放置的那个分区,而不是根目录。不要搞错了!以鸟哥的案例来说,我的根目录为/dev/hda2 而/boot 独立为/dev/hda1,因为与/boot 有关,所以磁盘代号就会成为(hd0,0)。

kernel:至于kernel后面接的则是内核的文件名,而在文件名后面接的则是内核的参数。由于启动过程中需要挂载根目录,因此kernel后面接的那个root=LABEL=/1指的是Linux的根目录在哪个分区的意思。还记得第 8 章谈过的 LABEL 挂载功能吧?是的,这里使用LABEL来挂载根目录。至于rhgb为色彩显示而quiet则是安静模式(屏幕不会输出内核检测的信息)。

initrd:就是前面提到的initrd 制作出RAM Disk 的文件名。

  1. 直接指定分区与文件名,不需要额外指定内核文件所在设备代号

kernel (hd0,0)/vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet

initrd (hd0,0)/initrd-2.6.18-92.el5.img

老实说,鸟哥比较喜欢这种样式的文件名写法,因为这样我们就能够知道内核文件所对应设备内的哪一个文件,而不会去想到我们的根目录(/,root)。让我们来想想/boot 有独立分区与无独立分区的情况吧!

figure_0609_0342

我的系统分区是/dev/hda1(/)、/dev/hda2(swap)而已,且我的内核文件为/boot/vmlinuz,请问grub的menu.lst内该如何编写内核文件位置?

答:我们使用迭代的方式来了解一下好了。由于内核文件名为/boot/vmlinuz,转成设备文件名与代号会成为如下的过程:

源文件 :/boot/vmlinuz↓

Linux 设备 :(/dev/hda1)/boot/vmlinuz↓

grub 设备 :(hd0,0)/boot/vmlinuz

所以最终的kernel写法会变成:

kernel(hd0,0)/boot/vmlinuz root=/dev/hda1…

figure_0609_0343

同上,只是我的分区情况变成/dev/sda1(/boot)、/dev/sda5(/)时呢?

答:由于/boot被独立出来了,所以情况会不一样。如下所示:

源文件 :/boot/vmlinuz↓

Linux 设备 :(/dev/sda1)/vmlinuz↓

grub 设备 :(hd0,0)/vmlinuz

所以最终的kernel写法会变成:

kernel(hd0,0)/vmlinuzroot=/dev/sda5…

2.利用chain loader 的方式转交控制权

所谓的 chain loader(引导装载程序的连接)仅是在将控制权交给下一个 boot loader 而已,所以grub并不需要“认识”与找出kernel的文件名,它只是将boot的控制权交给下一个boot sector 或MBR 内的boot loader 而已,所以通常它也不需要去检查下一个 boot loader的文件系统!

一般来说,chain loader 的设置只要两个就够了,一个是预计要前往的 boot sector 所在的分区代号,另一个则是设置 chain loader 在那个分区的 boot sector(第一个扇区)上!假设我的 Windows 分区在/dev/hda1,且我又只有一块硬盘,那么要 grub 将控制权交给Windows 的 loader 只要这样就够了:

[root@www~]# vi /boot/grub/menu.lst

….前略….

title Windows partition

root (hd0,0) <==设置使用此分区

chainloader +1 <== +1 可以想成第一个扇区,即是 boot sector

上面的范例中,我们可以很简单地这样想:那个 (hd0,0)就是 Windows 的 C 盘所在磁盘,而 chainloader+1 就是让系统加载该分区当中的第一个扇区(就是 boot sector)内的引导装载程序。不过,由于Windows的系统盘需要设置为活动(active)状态,且我们的grub默认会去检验该分区的文件系统,因此我们可以重新将上面的范例改写成这样:

[root@www ~]# vi /boot/grub/menu.lst

….前略….

title Windows partition

rootnoverify (hd0,0) <==不检验此分区

chainloader +1

makeactive   <==设置此分区为活动的(active)

grub的功能还不止此,它还能够隐藏某些分区。举例来说,我的/dev/hda5是安装Linux的分区,我不想让 Windows 能够认识这个分区时,你可以这样做:

[root@www ~]# vi /boot/grub/menu.lst

….前略….

title Windows partition

hide (hd0,4)  <==隐藏 (hd0,4) 这个分区

rootnoverify (hd0,0)

chainloader +1

makeactive

20.3.3 initrd 的重要性与创建新 initrd 文件

我们在本章稍早之前“boot loader 与 kernel 加载”的地方已经提到过 initrd,它的目的在于提供启动过程中所需要的最重要内核模块,以让系统启动过程可以顺利完成。会需要 initrd 的原因,是因为内核模块放置于/lib/modules/$(uname -r)/kernel/当中,这些模块必须要根目录(/)被挂载时才能够被读取。但是如果内核本身不具备磁盘的驱动程序时,当然无法挂载根目录,也就没有办法取得驱动程序,因此造成两难的地步。

initrd 可以将/lib/modules/….内的启动过程当中一定需要的模块打包成一个文件(文件名就是initrd),然后在启动时通过主机的 INT13 硬件功能将该文件读出来解压缩,并且 initrd 在内存内会仿真成为根目录,由于此虚拟文件系统(Initial RAM Disk)主要包含磁盘与文件系统的模块,因此我们的内核最后就能够认识实际的磁盘,那就能够进行实际根目录的挂载。所以说:initrd内所包含的模块大多是与启动过程有关,而主要以文件系统及硬盘模块(如usb、SCSI等)为主的。

一般来说,需要initrd的时刻为:

根目录所在磁盘为SATA、USB或SCSI等连接接口;

根目录所在文件系统为LVM、RAID等特殊格式;

根目录所在文件系统为非传统Linux“认识”的文件系统时;

其他必须要在内核加载时提供的模块。

之前鸟哥忽略 initrd 这个文件的重要性,是因为鸟哥很穷。因为鸟哥的 Linux 主机都是较早期的硬件,使用的是 IDE 接口的硬盘,而且并没有使用 LVM 等特殊格式的文件系统,而 Linux 内核本身就认识 IDE 接口的磁盘,因此不需要 initrd 也可以顺利启动完成的。自从 SATA 硬盘流行起来后,没有 initrd 就没办法启动了! 因为 SATA 硬盘使用的是 SCSI 模块来驱动的,而 Linux 默认将 SCSI 功能编译成为模块。

一般来说,各distribution提供的内核都会附上initrd文件,但如果你有特殊需要所以想重制initrd文件的话,可以使用 mkinitrd 来处理的。这个文件的处理方式很简单,man mkinitrd 就知道了!我们还是简单介绍一下!

[root@www ~]# mkinitrd [-v] [—with=模块名称] initrd文件名 内核版本

参数:

-v :显示 mkinitrd 的运行过程

—with=模块名称:模块名称指的是模块的名字而已,不需要填写文件名。举例来说,

目前内核版本的 ext3 文件系统模块为下面的文件名:

/lib/modules/$(uname -r)/kernel/fs/ext3/ext3.ko

那你应该要写成“—with=ext3”就好了 (省略 .ko)。

Initrd文件名:你所要创建的 initrd 文件名,尽量取有意义又好记的名字。

内核版本 :某一个内核的版本,如果是目前的内核则是“$(uname -r)”。

范例一:以 mkinitrd 的默认功能创建一个 initrd 虚拟磁盘文件

[root@www ~]# mkinitrd -v initrd_$(uname -r) $(uname -r)

Creating initramfs

Looking for deps of module ehci-hcd

Looking for deps of module ohci-hcd

….(中间省略)….

Adding module ehci-hcd <==最终加入 initrd 的就是下面的模块

Adding module ohci-hcd

Adding module uhci-hcd

Adding module jbd

Adding module ext3

Adding module scsi_mod

Adding module sd_mod

Adding module libata

Adding module pata_sis

[root@www ~]# ll initrd_*

-rw———- 1 root root 2406443 Apr 30 02:55 initrd_2.6.18-92.el5

由于目前的内核版本可使用 uname -r 取得,因此鸟哥使用较简单的命令来处理。

此时 initrd 会被创建起来,你可以将它移动到 /boot 等待使用。

范例二:增加 8139too 这个模块的 initrd 文件

[root@www ~]# mkinitrd -v —with=8139too initrd_vbirdtest $(uname -r)

….(前面省略)….

Adding module mii

Adding module 8139too <==看到没?这样就加入了!

initrd创建完成之后,同时内核也处理完毕后,我们就可以使用grub新建菜单了!下面继续瞧一瞧吧!

20.3.4 测试与安装grub

如果你的Linux主机本来就是使用grub作为loader的话,那么你就不需要重新安装grub了,因为grub本来就会主动去读取配置文件。你说是吧!但如果你的Linux原来使用的并非grub,那么就需要来安装。如何安装呢?首先,你必须要使用 grub-install 将一些必要的文件复制到/boot/grub 里面去,你应该这样做的:

安装些什么呢?因为 boot loader 有两个 stage ,而配置文件得要放置到适当的地方。这个 grub-install 就是在安装配置文件 (包括文件系统定义文件与 menu.lst 等) 。 如果要将 grub 的 stage1 主程序安装起来,就得要使用 grub shell 的功能。本章稍后会介绍。

[root@www~]#grub-install[—root-directory=DIR] INSTALL_DEVICE

参数:

—root-directory=DIR 那个 DIR 为实际的目录,使用 grub-install 默认会将

grub 所有的文件都复制到 /boot/grub/* ,如果想要复制到其他目录与设备去,

就得要用这个参数。

INSTALL_DEVICE 安装的设备代号。

范例一:将 grub 安装在目前系统的 MBR 下面,我的系统为 /dev/hda:

[root@www ~]# grub-install /dev/hda

因为原本 /dev/hda 就是使用 grub ,所以似乎不会出现什么特别的信息。

如果去查阅一下 /boot/grub 的内容,会发现所有的文件都更新了,因为我们重装了!

范例二:我的 /home 为独立的 /dev/hda3 ,如何安装 grub 到 /dev/hda3 (boot sector)

[root@www ~]# grub-install —root-directory=/home /dev/hda3

Probing devices to guess BIOS drives. This may take a long time.

Installation finished. No error reported.

This is the contents of the device map /home/boot/grub/device.map.

Check if this is correct or not. If any of the lines is incorrect,

fix it and re-run the script 'grub-install'.

(fd0) /dev/fd0

(hd0) /dev/hda <==会给予设备代号的对应表!

[root@www ~]# ll /home/boot/grub/

-rw-r—r— 1 root root 30 Apr 30 11:12 device.map

-rw-r—r— 1 root root 7584 Apr 30 11:12 e2fs_stage1_5

….(下面省略)….

看!文件都安装进来了!但是注意到,我们并没有配置文件。那要自己创建。

所以说,grub-install 是安装 grub 相关的文件(例如文件系统定义文件)到你的设备上面去等待在启动时被读取,但还需要设置好配置文件(menu.lst)后,再以grub shell 来安装grub 主程序到 MBR或者是boot sector 上面去。好了,那我们来思考一下想要安装的数据。

figure_0612_0344

我预计启动时要直接显示菜单,且菜单倒数为30秒。另外,在原本的menu.lst当中新增三个启动菜单,分别说明如下:

1.假设/dev/hda1 内含有 boot loader,此 loader 如何取得控制权?

2.如何重新读取MBR内的loader?

3.利用你原本的系统内核文件,新建一个可强制进入单用户维护模式的菜单。

答:第一点很简单,就利用上一小节的说明来处理即可。至于第二点,MBR的读取的是整块硬盘的第一个扇区,因此 root(hd0)才是对的。第三点则与内核的后续参数有关。整个文件可以被改写成这样:

[root@www~]# vim /boot/grub/menu.lst

default=0

timeout=30

splashimage=(hd0,0)/grub/splash.xpm.gz

hiddenmenu

title CentOS (2.6.18-92.el5)

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet

initrd /initrd-2.6.18-92.el5.img

title /dev/hda1 boot sector <==本例中的第一个新增菜单

root (hd0,0)

chainloader +1

title MBR loader   <==新增的第二个菜单

root (hd0)  <==MBR 为整块磁盘的第一个扇区,所以用整块磁盘的代号

chainloader +1

title single user mode  <==新增的第三个菜单(其实由原本的title复制来的)

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet single

initrd /initrd-2.6.18-92.el5.img

下次启动时,你就会发现有四个菜单可以选择,而默认会以第一个菜单来启动。

我们已经将配置文件处理完毕,但是你要知道的是,我们并不知道/dev/hda1到底有没有包含grub的主程序,因此我们想要将grub主程序再次安装到/dev/hda1的boot sector,也想要重新安装grub 到MBR 上面去。此时我们就得要使用 grub shell。整个安装与 grub shell 的操作其实很简单,如果你有兴趣研究的话,可以使用 info grub 去查阅。鸟哥这里仅介绍几个有用的命令而已。

用“root(hdx,x)”选择含有grub目录的那个分区代号;

用“find/boot/grub/stage1”看看能否找到安装信息文件;

用“find/boot/vmlinuz”看看能否找到内核文件(不一定要成功);

用“setup(hdx,x)”或“setup(hdx)”将grub 安装在boot sector 或MBR 中;

用“quit”来离开grub shell!

由于我们最需要安装的就是那个stage1。那才是grub的主程序。而且配置文件通常与主程序摆在同一个目录下。因此我们需要使用root(hd0,0)去找到/boot/grub/stage1。接下来,请用grub来进入 grub shell 吧!进入 grub 后,会出现一个“grub>”的提示符。

[root@www ~]# grub

1. 先设置一下含有 grub 目录的那个分区。

grub> root (hd0,0)

Filesystem type is ext2fs, partition type 0x83

鸟哥主机的分区中,/boot/grub 在 /boot 的分区(即是 /dev/hda1)内。

另外,grub 也能够分辨出该分区的文件系统 (ext2)。

2. 查找一下是否存在 stage1 这个信息文件

grub> find /boot/grub/stage1

(hd0,2)

见鬼!怎么会只有一个我们明明有 /boot/grub 与 /home/boot/grub 啊!

因为 /boot 是独立的,因此要找到该文件名就得要用如下的方式:

grub> find /grub/stage1

(hd0,0)

嗌这样就能够找到 !要特别注意 grub 找到的不是目录树,而是设备内的文件。

3. 查找一下是否可以找到内核/boot/vmlinuz-2.6.18-92.el5 ?

grub> find /boot/vmlinuz-2.6.18-92.el5

Error 15: File not found

grub> find /vmlinuz-2.6.18-92.el5

(hd0,0)

再次强调,因为 /boot/ 是独立的,因此就会变成上面的模样。

4. 将主程序安装上去吧!安装到 MBR 看看!

grub> setup (hd0)

Checking if "/boot/grub/stage1" exists… no <==因为 /boot 是独立的

Checking if "/grub/stage1" exists… yes <==所以这个文件名才是对的!

Checking if "/grub/stage2" exists… yes

Checking if "/grub/e2fs_stage1_5" exists… yes

Running "embed /grub/e2fs_stage1_5 (hd0)"… 15 sectors are embedded.

succeeded

Running "install /grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/grub/stage2

/grub/grub.conf"… succeeded <==将 stage1 程序安装妥当。

Done.

很好!确实有装起来,这样 grub 就在 MBR 当中了!

5. 那么重复安装到我的 /dev/hda1 呢?即是 boot sector 当中?

grub> setup (hd0,0)

Checking if "/boot/grub/stage1" exists… no

Checking if "/grub/stage1" exists… yes

Checking if "/grub/stage2" exists… yes

Checking if "/grub/e2fs_stage1_5" exists… yes

Running "embed /grub/e2fs_stage1_5 (hd0,0)"… failed (this is not fatal)

Running "embed /grub/e2fs_stage1_5 (hd0,0)"… failed (this is not fatal)

Running "install /grub/stage1 (hd0,0) /grub/stage2 p /grub/grub.conf "…

succeeded

Done.

虽然无法将 stage1_5 安装到 boot sector 去,不过,还不会有问题,

重点是最后面那个 stage1 要安装后显示 succeeded 字样就可以了!

grub> quit

如此一来,就已经将 grub 安装到 MBR 及/dev/hda1 的 boot sector 里面去了!而且读取的是(hd0,0)里面的/grub/menu.lst那个文件。真是很重要啊!

最后总结一下:

1.如果是从其他boot loader 转成grub时,得先使用grub-install 安装grub 配置文件;

2.开始编辑menu.lst这个重要的配置文件;

3.通过grub 来将主程序安装到系统中,如MBR 的(hd0)或boot sector 的(hd0,0)等。

20.3.5 启动前的额外功能修改

事实上,上一个小节设置好之后,你的 grub 就已经在你的 Linux 系统上面了,而且同时存在于MBR 与 boot sector 当中呢!所以,我们已经可以重新启动来查阅看看。另外,如果你正在进行启动,那么请注意,我们可以在默认菜单(鸟哥的范例当中是30秒)按下任意键,还可以进行grub的“在线编辑”功能。真是棒!先来看看启动界面吧!如图20-4所示。

figure_0614_0345

图20-4 grub 启动界面示意图

由于鸟哥将隐藏菜单的功能取消了,因此你会直接看到这四个菜单,同时会有读秒的倒数。菜单部分的界面其实就是title后面的文字。你现在知道如何修改title后面的文字了吧!如果你使用上下键去选择第二(/dev/hda1 boot sector)或第三(MBR loader)时,会发现同样的界面重复出现。这是因为那两个是 loader 移交而已。而我们都使用相同的 grub 与相同的 menu.lst 配置文件。因此这个界面就会重复出现了。

另外,如果你再仔细看的话,会发现到上图中底部还有一些细部的选项,似乎有个'e'edit的样子!没错,grub 支持在线编辑命令。这是个很有用的功能!假如刚才你将 menu.lst 的内容写错了,导致出现无法启动的问题时,我们可以查阅该title菜单的内容并加以修改。举例来说,我想要知道第一个菜单的实际内容时,将光带移动到第一个菜单,再按下'e'会进入如图20-5所示的界面。

figure_0615_0346

图20-5 grub 单一菜单内容

这不就是我们在menu.lst里面设置的东西吗?没错!此时你还可以继续进一步修改。注意看到上图最下面的说明,你还可以使用:

e:进入 grub shell 的编辑界面;

o:在光标所在行下面再新增一行;

d:将光标所在行删除。

我们说过,grub是可以直接使用内核文件来启动的,所以,如果你很清楚地知道你的根目录(/)在哪个分区,而且知道你的内核文件名(通常都会有个/boot/vmlinuz 连接到正确的文件名),那么直接在图20-5的界面当中以上述的o,d,e三个按键来编辑,如图20-6所示。

figure_0615_0347

图20-6 grubedit的在线编辑功能

按下[Enter]键后,然后输入b来boot,就可以启动啦!所以说,万一你的/boot/grub/menu.lst设置错误,或者是因为安装的缘故,或者是因为内核文件的缘故,导致无法顺利启动时,记得,可以在 grub 的菜单部分使用 grub shell 的方式去查询(find)或者是直接指定内核文件,就能够启动啦!

另外,很多时候我们的 grub 可能会发生错误,导致连 grub 都无法启动,那么根本就无法使用grub的在线编辑功能。怎么办?没关系啊!我们可以利用具有grub启动的CD来启动,然后再以CD的grub的在线编辑,同样可以使用硬盘上面的内核文件来启动。

20.3.6 关于内核功能当中的vga 设置

事实上,你的 tty1~tty6 除了 80x24 的分辨率外,还能够有其他分辨率的支持。但前提是你的内核必须支持FRAMEBUFFER_CONSOLE 这个内核功能参数才行。如何确定有没有支持呢?你可以查阅/boot/config-2.6.18-92.el5这个文件,然后这样查找:

[root@www~]# grep 'FRAMEBUFFER_CONSOLE' /boot/config-2.6.18-92.el5

CONFIG_FRAMEBUFFER_CONSOLE=y

这个项目如果出现 y 那就是有支持啦!如果被批注或是 n ,那就是没支持。

那么如何调整 tty1~tty6终端机的分辨率呢?先参考下面的表20-3再说(此为十进制数值)。

表20-3

figure_0616_0348

假设你想要将你的终端机屏幕分辨率调整到1024x768,且颜色深度为15bit色的时候,就得要指定vga=790那个数字。举例来说,鸟哥的tty1就想要这样的分辨率时,你可以这样做:

[root@www~]# vim /boot/grub/menu.lst

….(前面省略)….

title CentOS (2.6.18-92.el5)

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet vga=790

initrd /initrd-2.6.18-92.el5.img

….(后面省略)….

重新启动并选择此菜单进入Linux,你跑到tty1去看看,就已经是1024x768的分辨率。只是字会变得很小,但是界面的范围会加大就是了。不过,某些版本支持的是十六进制,所以还需要修改一下格式。一般使用上表当中的值应该就可以了。不过,由于不同的操作系统与硬件可能会有不一样的情况,因此,上面的值不见得一定可以在你的机器上面测试成功,建议你可以分别设置看看,以找出可以使用的值。

20.3.7 BIOS 无法读取大硬盘的问题

现今的硬盘容量越来越大,如果你使用旧的主板来安插大容量硬盘时,可能由于系统 BIOS 或者是其他问题,导致BIOS无法判断该硬盘的容量,此时你的系统读取可能会有问题。为什么呢?

我们在本章一开始的启动流程讲过,当进入Linux内核功能后,它会主动再去检测一下整个系统,因此BIOS识别不到的硬件在Linux内核反而可能会可以捉到而正常使用。举例来说,过去很多朋友经常会发现:系统使用DVD启动安装时,可以顺利安装好Linux,但是第一次启动时,屏幕只出现黑黑的一片,且出现grub>的字样,而无法进入Linux系统中,这又是怎么一回事?

在安装的过程中,由于是使用DVD或CD启动,因此加载Linux内核不成问题,而内核会去检测系统硬件,因此可以识别BIOS识别不到的硬盘,此时你确实可以安装Linux在大容量的硬盘上,且不会出现任何问题。

但是在进入硬盘启动时,由于kernel与initrd文件都是通过BIOS的INT13通道读取的,因此你的 kernel 与 initrd 如果放置在 BIOS 无法判断的扇区中,当然就无法被系统加载,而仅会出现grubshell(grub>)等待你的处理而已。

更多grub错误的代码查询可以到下面的链接查阅:

http://orgs.man.ac.uk/documentation/grub/grub_toc.html#SEC_Contents

现在你知道问题所在啦!那就是BIOS无法读取大容量磁盘内的kernel与initrd文件。那如何解决呢?很简单。就让kernel与initrd文件放置在大硬盘的最前头,由于BIOS至少可以读到大磁盘的1024柱面的数据,因此就能够读取内核与虚拟文件系统的文件。那如何让kernel与initrd放置到整块硬盘的最前面呢?简单得很。就新建/boot独立分区,并将/boot放置到最前面即可。更多其他的解决方案可参考文后的扩展阅读。 [4]

万一你已经安装了Linux且发生了上述的问题,那该怎办?你可以这样:

最简单的做法,就是直接重装,并且制作出/boot挂载的分区,同时确认该分区是在1024cylinder之前才行。

如果实在不想重装,没有关系,利用我们刚才上头提到的grub功能,额外新建一个可启动软盘,或者是直接以光驱启动,然后以grub的编写能力进入Linux。

另外的办法其实是骗过BIOS,直接将硬盘的cylinder,head,sector等信息直接写到BIOS当中去,如此一来你的BIOS可能就可以读取与支持你的大硬盘了。

不过,鸟哥还是建议你可以重新安装,并且制作出/boot这个分区。这也是为什么这一版中,鸟哥特别强调要分出/boot这个分区的原因。

20.3.8 为某个菜单加上密码

想象这么一个情景,如果你管理的是一间计算机教室,这间计算机教室是可对外开放的,但是你又担心某些分区被学生不小心弄乱,因此你可能会想要将某些启动菜单作个别保护。这个时候,为每个菜单做个加密的密码就是个可行的方案。那如何在启动的过程里面提供密码保护呢?首先,你必须要创建密码,而且还需要是加密过后的,否则人家跑到/boot/grub/menu.lst不就可以探查到你的启动密码了?那如何创建加密的密码呢?我们可以通过grub提供的md5编码来处理的,如下所示:

[root@www ~]# grub-md5-crypt

Password: <==输入密码

Retype password: <==再输入一次

$1$kvlI0/$byrbNgkt/.REKPQdfg287. <==这就是产生的 md5 密码!

上面产生的最后一行,由$开始到.结束的那行,就是你的密码经过md5编码过后的。将这个密码复制下来吧!假设我们要将第一个选项加入这个密码,而第四个选项加入另外的密码,那你应该要这样做:

[root@www~]# vim /boot/grub/menu.lst

….(前面省略)….

title CentOS (2.6.18-92.el5)

password —md5 $1$kvlI0/$byrbNgkt/.REKPQdfg287.

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet vga=790

initrd /initrd-2.6.18-92.el5.img

….(中间省略)….

title single user mode

password —md5 $1$GFnI0/$UuiZc/7snugLtVN4J/WyM/

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet single

initrd /initrd-2.6.18-92.el5.img

上面的案例中,我们两个菜单进入的密码并不相同,可以进行同样的分类。不过这样也造成一个问题,那就是一定要输入密码才能够进入启动流程,如果你在远程使用reboot重新启动,并且主机前面并没有任何人的话,你的主机并不会主动进入启动程序喔!

你必须要注意的是:password 这个选项一定要处在 title 下面的第一行。不过,此项功能还是可能被破解的,因为用户可以通过编辑模式(e)进入菜单,删除密码字段并按下 b 就能够进行启动流程了!那怎办?只好通过整体的password(放在所有的title之前),然后在title下面的第一行设置lock,那用户想要编辑时,也得要输入密码才行。设置有点像这样:

[root@www ~]# vim /boot/grub/menu.lst

default=0

timeout=30

password —md5 $1$kvlI0/$byrbNgkt/.REKPQdfg287. <==放在整体设置处

splashimage=(hd0,0)/grub/splash.xpm.gz

hiddenmenu

title CentOS (2.6.18-92.el5)

lock <==多了死锁的功能

root (hd0,0)

kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/1 rhgb quiet vga=790

initrd /initrd-2.6.18-92.el5.img

那么重新启动后,界面如图20-7所示。

figure_0618_0349

图20-7 grub 加密的示意图

你可以看到最下方仅出现 p 的功能,由于 2, 3, 4 菜单并没有使用 lock,因此这三个菜单用户还是可以执行启动程序,但是第一个菜单由于有lock选项,因此除非你输入正确的密码,否则第一个菜单是无法被加载执行的。另外,这个项目也能够避免你的menu.lst在启动的过程中被乱改,是具有保密menu.lst的功能,与刚才的菜单密码功能不同。

20.4 启动过程的问题解决

很多时候,我们可能因为做了某些设置,或者是因为不正常关机(例如未经通知的停电等)而导致系统的文件系统错乱,此时,Linux 可能无法顺利启动成功,那怎么办呢?难道要重装?当然不需要。进入runlevel1(单用户维护模式)去处理,应该就OK啦!下面我们就来谈一谈如何处理几个常见的问题!

20.4.1 忘记 root 密码的解决之道

大家都知道鸟哥的记忆力不佳,容易忘东忘西的,那如果连root的密码都忘记了,怎么办?其实在Linux环境中root密码忘记时还是可以救回来的!只要能够进入并且挂载/,然后重新设置一下root的密码,就救回来啦!这是因为启动流程中,若强制内核进入 runlevel 1 时,默认是不需要密码即可取得一个root的shell来救援的。整个操作有点像这样:

1.重新启动,一定要重新启动!

2.启动进入grub菜单后,在你要进入的菜单上面点'e'进入详细设置;将光标移动到kernel上方并点'e'进入编辑界面;然后出现如下界面来处理:

grub edit> kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/ rhgb quiet single

重点就是那个特殊字体。按下[enter]再按下b就能够启动进入单用户维护模式了。

3.进入单用户维护模式后,系统会以root的权限直接给你一个shell,此时你就能够执行“passwd”这个命令来重建root的密码。然后直接执行“init 5”就可以切换成为X窗口界面。就是这么简单!

20.4.2 init 配置文件错误

前一个root密码挽救的方法其实可以用在很多地方,唯一一个无法挽救的情况,那就是/etc/inittab这个文件设置错误导致的无法启动!根据启动流程,我们知道 runlevel 0~runlevel 6 都会读取/etc/inittab 配置文件,因此你使用 single mode(runlevel 1)当然也是要读取/etc/inittab 来进行开机的。那既然无法进入单用户维护模式,就表示这题无解?非也非也,既然默认的 init 无法执行,那我们就告诉内核不要执行init,改调用bash啊!可以略过init吗?可以的,同样在启动进入grub后,同样在 grub edit 的情况下这样做:

grubedit> kernel /vmlinuz-2.6.18-92.el5 ro root=LABEL=/ rhgb quiet init=/bin/bash

因为我们指定了内核调用的第一个进程(init)变成/bin/bash,因此/sbin/init就不会被执行。又根据启动流程的说明,我们知道此时虽然可以利用root取得bash来工作,但此时除了根目录外,其他的目录都没有被挂载;根目录被挂载成为只读状态。因此我们还需要进行一些操作才行,如图 20-8所示。

figure_0619_0350

图20-8 略过 init的进程直接进入bash shell

鸟哥仅执行两个命令:mount –o remount、rw /用途是将根目录重新挂载成为可读写,至于mount -a则是参考/etc/fstab的内容重新挂载文件系统!此时你又可以启动进行救援的工作了!只是救援完毕后,你得要使用reboot重新启动一次才行!

20.4.3 BIOS 磁盘对应的问题(device.map)

由于目前硬盘很便宜,所以很多朋友就想说:“那我能不能将Windows安装在/dev/had中而Linux安装在/dev/hdb中,然后调整BIOS的启动设备顺序,如此则两套系统各有各的loader安装在名硬盘的MBR当中了!”。这个想法非常好,如此一来两者就不会互相干扰,因为每块磁盘的MBR有不同操作系统的 loader。问题是,grub 对磁盘的设备代号使用的是检测到的顺序啊!也就是说,你调整了BIOS磁盘启动顺序后,你的menu.lst内的设备代号就可能会对应到错误的磁盘上了。

没关系的,我们可以通过/boot/grub/device.map这个文件来写死每个设备对grub磁盘代号的对应关系。举例来说,鸟哥的这个文件内容如下:

[root@www ~]# cat /boot/grub/device.map

(fd0) /dev/fd0

(hd0) /dev/hda

如果你不清楚如何处理的话,也可以利用grub-install的功能。例如:

[root@www ~]# grub-install —recheck /dev/hda1

这样device.map就会主动被更新了!

20.4.4 因文件系统错误而无法启动

如果因为设置错误导致无法开机时,要怎么办啊?这就更简单了!最容易出错的设置而导致无法顺利启动的步骤通常就是/etc/fstab 这个文件了,尤其是用户在实践 Quota 时,最容易写错参数,又没有经过 mount -a 来测试挂载,就立刻直接重新启动,真要命!无法启动成功怎么办?这种情况的问题大多如图20-9所示。

figure_0620_0351

图20-9 文件系统错误的示意图

看到最后两行,它说可以输入root的密码继续加以救援。那请输入root的密码来取得bash并以mount –o remount、rw/将根目录挂载成可读写后继续处理吧!其实会造成上述界面可能的原因除了/etc/fstab 编辑错误之外,如果你曾经不正常关机后,也可能导致文件系统不一致(Inconsistent)的情况,也有可能会出现相同的问题。如果是扇区错乱的情况,请看上图中的第二行,fsck告知其实是/dev/md0 出错,此时你就应该要利用 fsck 去检测/dev/md0 才是!等到系统发现错误,并且出现“clear[Y/N]”时,输入“y”吧!

这个fsck的过程可能会很长,而且如果你的分区上面的文件系统有过多的数据损坏时,即使fsck完成后,可能因为伤到系统盘,导致某些关键系统文件数据的损坏,那么依旧是无法进入Linux的。此时,最好就是将系统当中的重要数据复制出来,然后重新安装,并且检验一下是否物理硬盘有损伤的现象才好。不过一般来说,不太可能会这样啦、通常都是fsck处理完毕后就能够顺利再次进入Linux了。

20.4.5 利用chroot 切换到另一块硬盘工作

仔细检查一下,你的Linux里面应该会有一个名为chroot的命令才对!这是什么?这是“change root directory”的意思。意思就是说,可以暂时将根目录移动到某个目录下,然后去处理某个问题,最后再离开该root而回到原本的系统当中。

举例来说,有两三个Linux系统在同一个主机上面,假设我的第一个Linux无法进入了,那么我可以使用第二个Linux启动,然后在第二个Linux系统下将第一个Linux挂载起来,最后用chroot变换到第一个Linux,就能够进入到第一个Linux的环境当中去处理工作了。

你同样也可以将你的Linux硬盘拔到另一个Linux主机上面去,然后用这个chroot来切换,以处理你的硬盘问题,那怎么做啊?很简单。

1.用尽任何方法,进入一个完整的 Linux 系统(runlevel 3 或 runlevel 5);

2.假设有问题的Linux磁盘在/dev/hdb1上面,且它整个系统的排列是:

挂载点  设备文件名

/  → /dev/hdb1

/var  → /dev/hdb2

/home  → /dev/hdb3

/usr  → /dev/hdb5

若如此的话,那么在我目前的这个 Linux 下面,我可以新建一个目录,然后可以这样做:

挂载点 设备文件名

/chroot/    /dev/hdb1

/chroot/var/  /dev/hdb2

/chroot/home/→ /dev/hdb3

/chroot/usr/ → /dev/hdb5

3.全部挂载完毕后,再输入“chroot /chroot”,你就会发现,根目录 (/)变成那个/dev/hdb1的环境了。

20.5 重点回顾

Linux不可随意关机,否则容易造成文件系统错乱或者是其他无法启动的问题。

启动流程主要是BIOS、MBR、Loader、kernel+initrd、/sbin/init等流程。

loader具有提供菜单、加载内核文件、转交控制权给其他loader等功能。

boot loader 可以安装在 MBR 或者是每个分区的 bootsector 区域中。

initrd可以提供内核在启动过程中所需要的最重要的模块(通常是与磁盘及文件系统有关的模块)。

init 的配置文件为/etc/initab,此文件内容可以设置默认 runlevel、系统初始化脚本、不同执行等级的服务启动等。

额外的设备与模块对应可写入/etc/modprobe.conf中。

内核模块的管理可使用 lsmod, modinfo, rmmod, insmod, modprobe 等命令。

modprobe 主要参考/lib/modules/$(uname -r)/modules.dep 的设置来加载与卸载内核模块。

grub的配置文件与相关文件系统定义文件大多放置于/boot/grub目录中,配置文件名为menu.lst。

grub对磁盘的代号设置与Linux不同,主要通过检测的顺序来给予设置,如(hd0)及(hd0,0)等。

menu.lst 内每个菜单与 titile 有关,而直接指定内核启动时,至少需要 kernel 及 initrd 两个文件。

menu.lst内设置loader控制权移交时,最重要的为chainloader+1这个选项。

若想要重建initrd,可使用mkinitrd处理。

重新安装 grub 到 MBR 或 boot sector 时,可以利用 grub shell 来处理。

若想要进入救援模式,可于启动菜单过程中,在kernel的选项后面加入“single”或“init=/bin/bash”等方式来进入救援模式。

我们可以对grub的各个菜单给予不同的密码。

20.6 本章习题

情境模拟题一

利用救援光盘来处理系统的错误导致无法启动的问题。

目标:了解救援光盘的功能;

前提:了解grub的原理,并且知道如何使用chroot功能;

需求:打字可以再加快一点啊!

这个部分鸟哥就不抓图了,请大家自行处理。假设你的系统出问题而无法顺利启动,此时拿出原版光盘,然后重新以光盘来启动你的系统。然后你应该要这样做:

1.利用光盘启动时,到了看到boot:的阶段,按下[F5]之后会看到可以输入的选项,此时请输入:

boot: linux rescue

就能够进入救援模式的检测了!

2.然后请选择语系与键盘对应,这个与安装过程是一模一样的,所以选择“English”与“us”即可!

3.接下来会问你是否需要启动网络,因为我们的系统是出问题要处理,所以不需要启动网络,选择“No”即可;

4.然后就进入救援光盘模式的文件系统查找了。这个救援光盘会去找出目前你的主机里面与CentOS 5.x 相关的操作系统,并将该操作系统设置成为一个 chroot 的环境等待你的处置!但是它会有三个模式可以选择,分别是:continue,继续成为可读写挂载;Read-Only,将检测到的操作系统变成只读挂载;Skip,略过这次的救援动作。在这里我们选择Continue吧!

5.然后系统会将检测到的信息通知你!一般来说,可能会在屏幕上显示类似这样的信息:“chroot/mnt/sysimage”。此时请按下OK吧!

6.按下OK后,系统会丢给你一个shell使用,先用df看一下挂载情况是否正确。若不正确请手动挂载其他未被挂载的分区。等到一切搞定后,利用chroot/mnt/sysimage来转成你原本的操作系统环境。等到你将一切出问题的地方都搞定,请重启系统,且取出光盘,用硬盘启动吧!

简答题部分

如何查看与修改runlevel呢?

我有个朋友跟我说,他想要让一个程序在 Linux系统下一开机就启动,但是在关机前会自动先结束该程序,我该怎么建议他?

万一不幸我的一些模块没有办法让Linux的内核捕获到,但是偏偏这个内核明明就有支持该模块,我要让该模块在启动的时候就被加载,那么应该写入哪个文件?

如何在grub启动过程当中,指定以“runlevel1”来启动?

由于一些无心之过,导致系统启动时,只要执行 init 就会产生错误而无法继续启动,我们知道可以在启动的时候,不要以 init 加载系统,可以转换第一个执行程序,假设我第一个执行程序想要改为/bin/bash,好让我自行维护系统(不同于 runlevel 1),该如何进行此工作?

如果你不小心先安装 Linux再安装 Windows 导致 boot loader 无法找到 Linux 的启动菜单,该如何挽救?

20.7 参考数据与扩展阅读

infogrub

GNU官方网站关于grub的说明文件:

http://www.gnu.org/software/grub/manual/html_node/

纯文本屏幕分辨率的修改方法:

http://phorum.study-area.org/viewtopic.php?t=14776