Linux 内核启动流程(转)

启动流程一览

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

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

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

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

  1. 加载 BIOS 的硬件资讯与进行自我测试,并依据配置取得第一个可启动的装置;
  2. 读取并运行第一个启动装置内 MBR 的 boot Loader (亦即是 grub, spfdisk 等程序);
  3. 依据 boot loader 的配置加载 Kernel ,Kernel 会开始侦测硬件与加载驱动程序;

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) 。 然后开始运行硬件侦测的初始化,并配置 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 啦!这样 boot loader 也就能够被运行罗!

我们知道每颗硬盘的第一个磁区内含有 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 的相关性会有点像下图:


图 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 主要的功能如下:

  • 提供菜单:使用者可以选择不同的启动项目,这也是多重启动的重要功能!
  • 加载核心文件:直接指向可启动的程序区段来开始操作系统;
  • 转交其他 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 来开始后续的正常启动流程。

如上图所示,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 的!

在核心完整的加载后,您的主机应该就开始正确的运行了

 

 转自 http://vbird.dic.ksu.edu.tw/linux_basic/0510osloader_1.php

posted @ 2017-04-13 21:20  飞舞的旋律  阅读(229)  评论(0编辑  收藏  举报