Linux系统启动过程详解

标签:
it |
分类: Linux |
1、Linux 的启动流程分析
在介绍Linux系统启动过程之前,先对Linux系统启动的过程大致的给大家归纳一下,好让大家在看本文之前有个大致的了解,然后带着问题往下看,效果会更好些。
主机系统开始运行后,此时Linux才会调用外部程序开始准备软件执行的环境,并且实际夹在所有系统所需要的软件。最后系统就会开始等待你的登录操作。简单来说,Linux系统启动的过程如下:
·
·
·
·
·
·
·
·
1.1、BIOS, boot loader 与 kernel 加载
1.1.1 BIOS, 启动自我测试与 MBR
要启动整个系统首先就得要让系统去加载
BIOS (Basic Input Output System),并通过BIOS
程序去加载 CMOS 的信息,并且通过
在读取这些信息后,BIOS
还会进行开机自检
由于系统软件大多放置到硬盘中。所以 BIOS 会指定启动的设备好让我们可以读取硬盘中的操作系统的内核文件。但由于不同的操作系统文件系统格式不相同,因此我们必须要以一个引导装载程序来处理内核文件加载 (load) 的问题, 因此这个启动引导装载程序就被称为 Boot Loader 了。那这个 Boot Loader 程序安装在哪里呢?就在启动设备的第一个扇区 (sector) 内,也就是我们上面谈到的 MBR (Master Boot Record, 主引导分区)。
那你会不会觉得很奇怪啊?既然内核文件需要
loader 来读取,那每个操作系统的 loader 都不相同, 这样的话 BIOS 又是如何读取 MBR 内的 loader
呢?很有趣的问题吧!其实 BIOS 是透过硬件的 INT 13 中断功能来读取 MBR 的,也就是说,只要 BIOS
能够检测的到你的硬盘
注意:每块硬盘的第一个扇区区内含有
446
B的
MBR 区域,那么如果主机上面有两块硬盘的话,系统会去哪块硬盘的
MBR 读取 boot loader 呢?这个就得要看 BIOS 的配置了。基本上,我们常常讲的“系统的
MBR”其实指的是
第一个启动设备的
MBR
1.1.2 Boot Loader 的功能
刚刚说到 Loader 的最主要功能是要认识操作系统的文件格式并据以加载内核到内存中去运行。由于不同操作系统的文件格式不一致,因此每种操作系统都有自己的 boot loader。用自己的 loader 才有办法载入内核文件。那问题就来了,大家应该有听说过多操作系统吧?也就是在一台主机上面安装多种不同的操作系统。既然必须要使用自己的 loader 才能够加载属于自己的操作系统内核,而系统的 MBR 只有一个,那你怎么会有办法同时在一部主机上面安装 Windows 与 Linux 呢?
http://s12/mw690/003E0ziMty71l8wCpflab&690
图1 Boot sector与操作系统的关系
如上图所示,每个操作系统默认是会安装一套 boot loader 到它自己的文件系统中
(就是每个 filesystem 左下角的方框),而在 Linux 系统安装时,你可以选择将引导扇区(boot
sector)安装到主引导分区(MBR)
到这里,我们刚刚提到的两个问题还是没有解决啊!虽然各个操作系统都可以安装一份 boot loader 到他们的引导扇区(boot sector)中,这样操作系统可以通过自己的 boot loader 来加载内核文件了。问题是系统的主引导分区(MBR)只有一个啊!你要怎么运行 引导扇区(boot sector)里面的 boot loader 啊?这里就得说一下boot loader的功能了。boot loader 主要的功能如下:
·
·
·
由于具有菜单功能,因此我们可以选择不同的内核来启动。而由于具有控制权转交的功能,因此我们可以加载其他引导扇区(boot sector)内的 boot loader 啦!不过 Windows 的 boot loader 默认不具有控制权转交的功能,因此不能使用 Windows 的 boot loader 来加载 Linux 的 loader哈!这也是在装多系统时,会特别强调先装 Windows 再装 Linux 的缘故。 我们将上述的三个功能用下图2来解释你就看的懂了:
http://s9/mw690/003E0ziMty71l8zowv638&690
图2引导装载程序的菜单功能与控制权转交功能示意图
上图中,MBR 使用 Linux 的 grub 这个引导装载程序,并且里面假设已经有了三个菜单,第一个菜单可以直接指向 Linux 的内核文件并且直接加载内核来启动;第二个菜单可以将引导装载程序控制权交给 Windows 来管理,此时 Windows 的 boot loader 会接管启动流程,这个时候它就能够启动 windows 了。第三个菜单则是使用 Linux 在 boot sector 内的引导装载程序,此时就会跳出另一个 grub 的菜单啦。
·
·
当我们通过
[root@localhost ~]# ls --format=single-column -F /boot
config-2.6.32-431.el6.x86_64
grub/
initrd-2.6.32-431.el6.x86_64kdump.img<==虚拟文件系统文件
System.map-2.6.32-431.el6.x86_64
vmlinuz-2.6.32-431.el6.x86_64*
从上面我们也可以知道此版本的 Linux 内核为2.6.32-431.el6.x86_64这个版本!为了硬件开发商与其他内核功能开发者的便利,因此 Linux 内核是可以通过动态加载内核模块的 (就请想成驱动程序即可),这些内核模块就放置在 /lib/modules/ 目录内。 由于模块放置到磁盘根目录内 (要记得 /lib 不可以与 / 分别放在不同的分区),因此在启动的过程中内核必须要挂载根目录,这样才能够读取内核模块提供加载驱动程序的功能。而且为了避免影响到磁盘内的文件系统,因此启动过程中根目录是以只读的方式来挂载。
一般来说,非必要的功能且可以编译成为模块的内核功能,目前的
Linux distributions 都会将它编译成为模块。因此
U盘, SATA, SCSI... 等磁盘设备的驱动程序通常都是以模块的方式来存在的。现在来思考一种情况,假设你的
linux 是安装在 SATA
磁盘上面的,你可以通过
问题是,内核根本不认识 SATA 磁盘,所以需要加载 SATA 磁盘的驱动程序,否则根本就无法挂载根目录。但是 SATA 的驱动程序在 /lib/modules 内,你根本无法挂载根目录,又怎么读取到 /lib/modules/ 内的驱动程序?是吧!是不是有点进退两难?在这个情况之下,你的 Linux 是无法顺利启动的! 那怎办?没关系,我们可以透过虚拟文件系统来处理这个问题。
虚拟文件系统 (Initial RAM Disk) 一般使用的文件名为
/boot/initrd ,这个文件的特色是,它也能够通过boot
loader 来加载到内存中, 然后这个文件会被解压缩并且在内存当中仿真成一个根目录,且此仿真在内存当中的文件系统能够提供一个可执行的程序,通过该程序来加载启动过程中所最需要的内核模块,通常这些模块就是
U盘, RAID, LVM, SCSI 等文件系统与磁盘接口的驱动程序。等载入完成后,会帮助内核重新调用
http://s10/mw690/003E0ziMty71l8BWmO5b9&690
图3 BIOS与boot loader及内核加载流程示意图
如上图3所示,boot loader 可以加载 kernel 与 initrd ,然后在内存中让 initrd 解压缩成为根目录, kernel 就能够借此加载适当的驱动程序,最终释放虚拟文件系统,并挂载实际的根目录文件系统,就能够开始后续的正常启动流程。更详细的 initrd 说明,你可以自行使用 man initrd 去查阅看看。 下面来看一下 CentOS 6.x 的 initrd 文件内容有什么吧!
http://s8/mw690/003E0ziMty71l8EXI6ra7&690
[root@localhost initrd]# cpio -ivcdu < initrd-2.6.32-431.el6.x86_64kdump命令对其进行解压缩,解压完毕后看看 initrd-2.6.32-431.el6.x86_64kdump目录有什么东西:
http://s16/mw690/003E0ziMty71l8H70iraf&690看!是不是很像根目录?接下来用vim init来这个文件看看都有些什么内容:
mount -t proc /proc /proc
.....(中间省略).....
echo Creating initial device nodes <==新建系统所需的各项设备
mknod /dev/null c 1 3
.....(中间省略).....
echo "Loading dm-mod.ko module"
insmod /lib/modules/2.6.32-431.el6.x86_64/dm-mod.ko
.....(中间省略).....
通过上述执行文件的内容,我们可以知道 initrd 有加载模块并且尝试挂载了虚拟文件系统。 接下来就能够顺利的运行啦!那么是否一定需要 initrd 呢?
不一定的,需要
initrd 最重要的原因是,当启动时无法挂载根目录的情况下,此时就一定需要 initrd
,例如你的根目录在特殊的磁盘介面
(U盘, SATA, SCSI) ,或者是你的文件系统较为特殊 (LVM, RAID) 等等,才会需要
initrd。
如果你的
Linux 是安装在 IDE
接口的磁盘上,并且使用默认的
ext2/ext3 文件系统,那么不需要 initrd 也能够顺利的启动进入 Linux 的!
在内核完整的加载后,您的主机应该就开始正确的运行了,接下来,就是要开始运行系统的第一支程序:/sbin/init。
1.2、第一个进程 init 及配置文件 /etc/inittab 与 runlevel
在内核加载完毕、进行完硬件检测与驱动程序加载后,此时你的主机硬件应该已经准备就绪了
(ready) ,此时内核会主动的调用第一个进程,那就是
/sbin/init咯。这也是为啥使用
那么什么是 run level 呢?它有什么功用?其实很简单,
Linux 就是通过设置
由于 run level 0, 4, 6 不是关机、重新启动就是系统保留的,所以当然不能将默认的 run level
设置为这三个值,否则系统就会不断的自动关机或自动重新启动....
好了,那么我们启动时,到底是如何取得系统的 run level 的?当然是 /etc/inittab
所设置。
http://s9/mw690/003E0ziMty71l8Kh1lm48&690
现在你会自行修改登陆时的默认 run level 设置值了吗?够简单的吧?一般来说,我们默认都是 3 或者是 5 来作为默认的 run level 的。但有时后可能需要进入 run level 1,也就是单用户维护模式的环境当中。这个 run level 1 有点像是 Windows 系统当中的“安全模式”,专门用来处理当系统有问题时的操作环境。此外,当系统发现有问题时,比如不正常关机造成 filesystem 的不一致现象时,系统会主动的进入单用户维护模式。
好了, init 在取得 run level 之后,接下来要干嘛? 那就是通过/etc/rc.d/rc.sysinit这个文件来初始化整个系统了。
1、3 init处理系统初始化流程(/etc/rc.d/rc.sysinit)
系统根据/ect/inittab文件获取到运行级别后,就会执行/etc/rc.d/rc.sysinit这个shell脚本来设置好整个系统的环境,如果用vim /etc/rc.d/rc.sysinit来打开这个文件的话,可以发现这个文件的主要工作有以下这些:
1、取得网络环境与主机类型
读取网络配置文件
2、测试与挂载内存设备
除挂载内存设备
3、决定是否启动
SELinux
SELinux
在此时进行一些检测, 并且检测是否需要帮所有的文件重新编写标准的 SELinux 类型 (autorelabel)。
4、启动系统的随机数产生器
随机数产生器可以帮助系统进行一些口令加密演算的功能,在此需要启动两次随机数产生器。
5、配置终端机
(console)
字体。
6、配置显示于启动过程中的欢迎界面
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、初始化软件磁盘阵列:主要是通过
13、初始化
LVM 的文件系统功能。
14、以
fsck 检验磁盘文件系统:会进行
filesystem check。
15、进行磁盘配额
quota 的转换
(非必要)。
16、重新以可读写模式挂载系统磁盘。
17、启动
quota 功能:所以我们不需要自定义
18、启动系统虚拟随机数产生器
(pseudo-random)。
19、清除启动过程当中的缓存文件。
20、将启动相关信息加载 /var/log/dmesg 文件中。
在 /etc/rc.d/rc.sysinit
将基本的系统配置数据都写好了,也将系统的数据配置完整!而如果你想要知道到底启动的过程中发生了什么事情呢?那么就运行“dmesg
”命令来查看吧。另外,基本上,在这个文件当中所进行的很多工作的默认配置文件其实都在
/etc/sysconfig/ 当中。
在这个过程当中,比较值得注意的是自定义模块的加载!在 CentOS 当中,如果我们想要加载内核模块的话,可以将整个模块写入/etc/sysconfig/modules/*.modules 当中,在该目录下,只要记得文件名最后是以 .modules 结尾即可。 这个过程是非必要的,因为我们目前的默认模块实在已经很够用了,除非是你的主机硬件实在太新了,非要自己加载新的模块不可,否则,在经过 /etc/rc.d/rc.sysinit 的处理后,你的主机系统应该是已经跑得很顺畅了,就等著你将系统相关的服务与网络服务启动了。
1、4启动系统服务与相关启动配置文件(/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 放在哪?请看下图:
http://s6/mw690/003E0ziMty71l8MqHL705&690
从run level 0-run level 6分别对应rc0.d-rc6.d目录。
由于我的系统的默认run level为3,所以就以rc3.d目录进行讲解好了。/etc/rc.d/rc
3
·
·
·
通过上面的说明我们可以知道所有的选项都与 /etc/rc3.d/ 有关,那么我们就来瞧瞧这个目录下有些什么玩意儿吧!
http://s16/mw690/003E0ziMty71l8Qdsab7f&690
这是/etc/rc3.d/目录下的文件,通过命令:ll /etc/rc3.d查看一下会更详细,如图:
http://s15/mw690/003E0ziMty71l8U7s067e&690
在这个目录下的文件很有趣,主要具有几个特点:
文件全部以 Sxx 或 Kxx ,其中 xx 为数字,且这些数字在文件之间是有相关性的!
全部是连接文件,连接到服务的启动目录 /etc/init.d/ 去
我们知道服务的启动主要是以“/etc/init.d
·
·
由此可见,当你有想要启动该 runlevel 时就运行的服务,那么利用 Sxx 并指向 /etc/init.d/ 的特定服务启动脚本后,该服务就能够在启动时启动。就这么简单!问题是,你需要自行处理这个 K, S 开头的连接文件吗?并不需要的,chkconfig(如:chkconfig httpd on #将httpd服务设置为开机启动)命令就是在负责处理这个连接文件啦!
那么为什么 K 与 S 后面要有数字呢?因为各不同的服务其实还是相互有关系的。举例来说,如果要启动 WWW 服务,总是得要有网络吧?所以 /etc/init.d/network 就会比较先被启动。那么你就会知道在 S 或者是 K 后面接的数字是啥意思了吧?嘿嘿,那就是服务的执行顺序啦!那么哪个文件被最后运行呢?看到最后一个被运行的选项是啥?没错,就是 S99local ,亦即是: /etc/rc.d/rc.local 这个文件啦!
1、5用户自定义开机启动程序(/etc/rc.d/rc.local)
在完成默认 runlevel 指定的各项服务的启动后,如果我还有其它的操作想要完成时,举例来说,我还想要发一封
mail 给某个系统管理帐号,通知他,系统刚刚重新启动完毕,那么是否应该要制作一个 shell script 放置在
/etc/init.d/ 里面,然后再以连接方式连接到
/etc/rc3.d/
里面呢?呵呵!当然不需要!可以在
也就是说,我有任何想要在启动时就进行的工作时,直接将它写入
/etc/rc.d/rc.local文件中
1、6 加载终端机或 X-Window 界面
在完成了系统所有服务的启动后,接下来 Linux 就会启动终端或者是 X-Window 来等待用户登陆。Linux系统默认是打开6个终端的,分别可以用ALT+F1到ALT+F6进行访问,6个终端默认都驻留在内存中,一般我们Linux 服务器都是通过远程登录,就算是本地登录,也只用到2个左右,其他可以关闭以节省内存。那么如何关闭这些默认开着的终端呢?在CentOS 5之前的版本,可以修改/etc/inittab文件,加#注释掉你不想要的终端即可;但是在 CentOS 6以后的版本中,修改方法略有不同,终端TTY的配置由之前CentOS 5版本的/etc/inittab文件更改为/etc/init/start-ttys.conf,执行以下命令可以将默认的6个TTY终端更改为3个,找到“tty[1-6]”改为“tty[1-3]”,如图:
http://s14/mw690/003E0ziMty71l8WppRHfd&690
保存退出,重启系统即可生效。
关于X-Window的启动流程建议大家自行学习,在这里就不再详述。
1、7 Run level的切换
在我们完成上面的所有信息后,其实整个
Linux 主机就已经在等待我们用户的登陆了。
事实上,与 run level 有关的启动其实是在 /etc/rc.d/rc.sysinit 运行完毕之后。也就是说,其实 run level 的不同仅是 /etc/rc[0-6].d 里面启动的服务不同而已。不过,依据启动是否自动进入不同 run level 的设置,我们可以得出以下结论:
1.
2.
假设原本我们是以 run level
3
·
·
·
也就是说,两个 run level 都存在的服务就不会被关闭。如此一来,就很容易切换
run level 了, 而且还不需要重新启动,真牛掰。那我怎么查看目前系统的runlevel及如何切换runlevel呢?
http://s1/mw690/003E0ziMty71l8YlcU8a0&690
查看及切换runlevel
到此为止,Linux的整个启动流程就结束啦,接下来就是用户的操作了,想干啥就干点啥吧!
说了这么多,总结一下:本文从主机的BIOS、boot loader、kernel的加载开始;到第一个init进程(Linux系统下所有进程的父进程)及配置文件/etc/inittab与runlevel,在这个小节介绍了系统调用init来准备软件执行的环境,并介绍各个runlevel的具体含义;接着介绍init处理系统初始化的相关流程(主要执行/etc/rc.d/rc.sysinit文件来实现);然后介绍了启动系统服务与相关启动配置文件(/etc/rc.d/rc N & /etc/sysconfig);再到用户如何实现自定义开机启动程序(主要通过修改/etc/rc.d/rc.local文件来实现)以及如何修改系统默认设置的tty终端数量(通过修改/etc/init/start-ttys.conf文件实现);最后介绍了在进行Run level切换时,相应的/etc/rc.d/rcN.d目录内文件的启动及关闭操作之间的关系。