关于计算机操作系统启动引导的总结

前言

  对于现代CPU而言,当其上电启动时,必须知道指令在什么地方,这是很简单的道理。现代大多数计算机的做法是将这第一段程序放到称为BIOS的固件(存储材料大都为EEPROM或FLASH芯片)里,该程序会根据配置信息读取磁盘(或其他存储设备,例如外接USB)的特定分区里的程序代码并执行,逐渐启动操作系统的所有组件。

  总之,在实际应用环境中,要综合考虑操作系统类型、固件类型以及磁盘分区类型这三个主要因素来部署解决 方案。

方案一: BIOS + MBR

1. BIOS(Baisc Input/output System)②③

BIOS是IBM PC兼容机的固件接口(注意与UEFI的区分)。其主要程序包括:

a. 中断例程:处理硬件中断

b. 系统设置程序:该程序负责设置BIOS参数,并将这些参数存储在CMOS RAM芯片中。CMOS中还包含其他一些配置信息(如系统时间),计算机不通电时,由一块主板上的后背电池来维持其信息。

c. POST上电自检:将包括CPU、640K基本内存、1M以上的扩展内存、ROM、主板、 CMOS存贮器、串并口、显示卡、软硬盘子系统及键盘测试。自检中若发现问题,系统将给出提示信息或鸣笛警告。

d.引导程序:在完成POST自检后,BIOS将按照系统CMOS设置中的启动顺序搜寻软硬盘驱动器及CDROM、网络服务器等有效的启动驱动器 ,读入操作系统引导记录,然后将系统控制权交给引导记录,由引导记录完成系统的启动。

2. MBR(Main Boot Record) ①

  通常我们将包含MBR的扇区称为主引导扇区(位置为0/0/1扇区,大小为512字节),其组成有三部分:

a. MBR(占用446字节),它用于在硬盘启动时将系统控制转给用户指定的并在分区表里登记的操作系统。现代启动装载程序的大小通常超出了 MBR 空间所能容纳的范围,因此必须采用多阶段设计,其中 MBR 部分只知道如何从其他位置加载下一阶段。

b. 磁盘分区表(DPT),由4个分区表项组成(每个16字节)。对于扩展分区类型,其16字节中包含的是其包含的第一个逻辑分区的信息,各逻辑分区呈链表结构。

c. 结束标志(占用2个字节),其值为0xAA55。

MBR程序段的执行流程:

a. 检查硬盘分区表是否完好。
b. 在分区表中查找可引导的活动分区。
c. 将活动分区的第一逻辑扇区装入内存。因此可以得知,当执行MBR程序段时,BIOS的任务已经结束了。

  一定要清楚的是,MBR程序段执行完成后,跳向哪里?应该有两个方向,一个是活动分区内操作系统的内核文件;另一个就是活动分区内的启动扇区(boot sector),进行任务转交。(如果使用GRUB,第三个方向应该是GRUB的stage1.5)
无奈的时,Windows在安装时会覆盖掉原有的MBR和自己所在分区的启动扇区,意思是‘你们别想启动其他系统!!!’,不过多重启动的方法多的是。其他具体细节见参考资料①。

3. GRUB所做的事情③

  Grub(下文都指GRUB 2)通常被使用于现代Linux系统的引导启动,但它很强大,支持多系统引导,且可以通过chain load引导专有系统(例如Windows)。另外,对于Linux系统,其还可以选择不同的kernel来引导。
下文便于说明,将GRUB所做的事情分为几个stage:

a. stage 1
通常stage 1就是MBR中的引导代码,具体对应的文件为boot.img。由于它太小,所以不够智能,也不能识别文件系统,而我们的内核文件是基于文件系统的,因此我们需要更多的代码,就是将控制权交给下一阶段。

b. stage 1.5
stage1.5位于磁盘主引导扇区和第一分区之间。这部分空间因为历史原因被保留,而GRUB巧妙地抓住这个空子(也许是糟糕的设计!!!)。有些磁盘的第一分区起始位置为第63扇区(有的则为第2048扇区,比如笔者所用的系统),因此stage1.5所能使用的空间大小为62*512 = 31722 bytes,(或许)足够容纳该阶段的文件core.img。stage1.5 现在可以识别一些文件系统,包括EXT,FAT,ntfs等。最后stage1.5就根据其支持的文件系统,寻找satge2的/boot目录文件并载入,将控制权转交。

c. stage 2
该阶段所有文件都位于/boot/grub2(或者/boot/grub)目录(和stage1/1.5一个单独的image文件不同)。主要工作是定位并载入被选择的内核文件,并将控制权转交给内核。内核及相关文件位于/boot目录下,内核文件几乎都以vmlinuz开头来命名,可以有多个。

d. kernel
在/boot目录下,除了内核文件,还包括initrd.image(initial RAM disk)和System.map文件等。initrd里包含各种可执行程序和驱动程序,用于挂载实际的根文件系统,initrd存在的原因等信息具体参考资料④。关于查看和创建initrd的信息具体参考资料⑤及⑥。在grub命令行里可以通过使用kernel和initrd来指定内核与initrd.img。

e.关于 initrd
initrd使得内核可以简单使用 Ramdisk 的能力,包括:

  • 格式化一个 ramdisk
  • 加载文件系统内容到 ramdisk
  • ramdisk作为根文件系统

在内核接管 CPU 后,然后内核调用 init() 函数(不是后来的 init 进程);然后调用 initrd_load() 在内存中加载 initrd 根文件系统(还句话说,就是将一部分内存虚拟化磁盘来使用,并在这个虚拟磁盘上创建根文件系统);然后调用 mount_root() 函数来创建真正的根文件系统,并进入该目录中;最后,调用run_init_process()函数,使用execve启动 init 进程。

方案二:GPT(GUID Partition Table)

  GPT是UEFI规范的一部分,但仍然可以运用到BIOS系统中。它作为MBR的替代解决方案而出现,因为MBR限制了分区的最大可寻址空间为2TB(232 × 512 bytes),以及其他一些固有的限制(比如CHS,512字节的扇区大小)。GPT为了保持向后兼容性,可以与MBR共存。下图是GUID规范中各字段的布局:

a. LAB 0
  为了减少分区表损坏的风险,GPT在硬盘最后保存了一份分区表的副本
在支持从GPT启动的操作系统中,这里(Protective MBR)也用于存储第一阶段的启动代码。在这个MBR中,只有一个标识为0xEE的分区,以此来表示这块硬盘使用GPT分区表。
在使用MBR/GPT混合分区表的硬盘中,这部分存储了GPT分区表的一部分分区(通常是前四个分区),可以使不支持从GPT启动的操作系统从这个MBR启动,启动后只能操作MBR分区表中的分区。如Boot camp就是使用这种方式启动Windows。
For GRUB to boot from a GPT-partitioned disk on a BIOS-based system, a BIOS Boot Partition is required.

b.LAB 1
  GPT分区表头,定义磁盘可用的块,另外也定义分区表项的数量和大小。
  详细查看维基百科⑪

c.LAB2-33
  该部分为分区表项,描述每个分区的详细信息,每个表项最少使用128个字节。其起始位置及每个表项的大小在分别在头部的72与84字节处定义。默认情况下分配的空间可以定义128个分区,如果需要更多的分区,则分配更多的空间并在GPT头部注明就可以了。

Grub2对BIOS/GPT的支持

  由于使用GPT分区表,grub的stage1.5就不能存在了,因为此时我们无法确保在MBR与第一分区之间会留有空间。在这种情况下,需要一个额外的BIOS Boot Partition分区存放grub的core.img。

分区工具总结列表:待补充

  关于GPT与BIOS方案的选择问题以及分区表的备份与还原可以参考ArchLinux Wiki Partitioning。

方案三:UEFI

  关于如何在计算机启动时屏蔽UEFI以及安全启动请查看Disable_UEFI_and_Secure_Boot

方案四:U-boot

  待补充

参考资料

MBR 百度百科
BIOS 百度百科
An introduction to the Linux boot and startup processes
详解 Linux Initrd
initrd 百度百科
mkinitcpio ArchLinux Wiki
Boot Loader ArchLinux Wiki
UEFI原理小结
EasyBCD 官方wiki
UEFI与BIOS对比
GPT 维基百科