下面将介绍计算机启动的流程,涉及Legacy BIOS和UEFI两种启动方式,为后面继续折腾引导打下基础。
从你按下电源键,到登录界面出现,期间发生了很多事,其中有一个叫bootloader的家伙就度过了它短暂的一生。
PC启动的过程可以分为三个阶段:
固件负责初始化一些设备,检查设备是否正常,然后按照设定的顺序,依次查找可启动的磁盘。找到之后,把磁盘上的bootloader加载到内存,运行bootloader。
bootloader负责把操作系统这个大家伙给拉起来干活,bootloader是用户自定义的程序,所以他的功能可以多种多样,比如选择启动哪个操作系统。
操作系统启动阶段差别就大了,毕竟各家操作系统有各家的本事,这里就不展开来讲了。
下面详细介绍一下固件阶段和bootloader阶段
这两个有什么区别?简单说就是Legacy BIOS是很久之前出现的,UEFI是之后出现的,有了很多新功能,设计也更科学,反正更高级就是了。
详细地说,UEFI支持下面的功能
BIOS就是Basic Input/Output System,基本输入输出系统,之所以前面加了个Legacy,是因为UEFI出来之后这货就没什么地位,下面简称BIOS。
BIOS的流程是这样的:
这里假设读者已经了解MBR格式的分区表结构。
查找可启动盘是依据每个磁盘的第一个扇区存放的MBR末尾的AA55H标志,如果有AA55H,说明该磁盘可启动。
举个例子,我Ubuntu所在的磁盘是/dev/sda
,使用下面的命令查看第一个分区的内容:
➤ sudo hd /dev/sda -n 512 23:45:22
00000000 eb 63 90 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 |.c..............|
00000010 fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00 |...|.........!..|
00000020 00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75 |....8.u........u|
00000030 f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 01 8b |.........|...t..|
00000040 4c 02 cd 13 ea 00 7c 00 00 eb fe 00 00 00 00 00 |L.....|.........|
00000050 00 00 00 00 00 00 00 00 00 00 00 80 01 00 00 00 |................|
00000060 00 00 00 00 ff fa 90 90 f6 c2 80 74 05 f6 c2 70 |...........t...p|
00000070 74 02 b2 80 ea 79 7c 00 00 31 c0 8e d8 8e d0 bc |t....y|..1......|
00000080 00 20 fb a0 64 7c 3c ff 74 02 88 c2 52 bb 17 04 |. ..d|<.t...R...|
00000090 f6 07 03 74 06 be 88 7d e8 17 01 be 05 7c b4 41 |...t...}.....|.A|
000000a0 bb aa 55 cd 13 5a 52 72 3d 81 fb 55 aa 75 37 83 |..U..ZRr=..U.u7.|
000000b0 e1 01 74 32 31 c0 89 44 04 40 88 44 ff 89 44 02 |..t21..[email protected]..D.|
000000c0 c7 04 10 00 66 8b 1e 5c 7c 66 89 5c 08 66 8b 1e |....f..\|f.\.f..|
000000d0 60 7c 66 89 5c 0c c7 44 06 00 70 b4 42 cd 13 72 |`|f.\..D..p.B..r|
000000e0 05 bb 00 70 eb 76 b4 08 cd 13 73 0d 5a 84 d2 0f |...p.v....s.Z...|
000000f0 83 d0 00 be 93 7d e9 82 00 66 0f b6 c6 88 64 ff |.....}...f....d.|
00000100 40 66 89 44 04 0f b6 d1 c1 e2 02 88 e8 88 f4 40 |@f.D...........@|
00000110 89 44 08 0f b6 c2 c0 e8 02 66 89 04 66 a1 60 7c |.D.......f..f.`||
00000120 66 09 c0 75 4e 66 a1 5c 7c 66 31 d2 66 f7 34 88 |f..uNf.\|f1.f.4.|
00000130 d1 31 d2 66 f7 74 04 3b 44 08 7d 37 fe c1 88 c5 |.1.f.t.;D.}7....|
00000140 30 c0 c1 e8 02 08 c1 88 d0 5a 88 c6 bb 00 70 8e |0........Z....p.|
00000150 c3 31 db b8 01 02 cd 13 72 1e 8c c3 60 1e b9 00 |.1......r...`...|
00000160 01 8e db 31 f6 bf 00 80 8e c6 fc f3 a5 1f 61 ff |...1..........a.|
00000170 26 5a 7c be 8e 7d eb 03 be 9d 7d e8 34 00 be a2 |&Z|..}....}.4...|
00000180 7d e8 2e 00 cd 18 eb fe 47 52 55 42 20 00 47 65 |}.......GRUB .Ge|
00000190 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 65 61 |om.Hard Disk.Rea|
000001a0 64 00 20 45 72 72 6f 72 0d 0a 00 bb 01 00 b4 0e |d. Error........|
000001b0 cd 10 ac 3c 00 75 f4 c3 22 11 0e 44 00 00 80 20 |...<.u.."..D... |
000001c0 21 00 83 fe ff ff 00 08 00 00 00 e8 7c 0d 00 fe |!...........|...|
000001d0 ff ff 05 fe ff ff fe f7 7c 0d 02 50 7c 00 00 00 |........|..P|...|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200
# 上面最后的55aa就是启动标志,为什么不是aa55?大小端嘛,普通PC默认的是小端字节序
找到可以启动的磁盘之后,BIOS会把这个磁盘的第一个扇区,也就是MBR,加载到内存中执行。
加载到内存的MBR中包含了bootloader,或者说包含了bootloader的一部分,固件阶段完成。
之所以说一部分,是因为MBR大小只有512B,而真正能用于存bootloader代码的只有512B-4x16B-2=446B
(减掉4个分区表项,2B启动标志),这446B的代码要负责把操作系统给拉起来,实在有点力不从心。于是bootloader的代码就无法全部放在MBR中,只放了一部分代码,这部分代码的任务就是把存储在磁盘其他位置的bootloader的剩余部分加载到内存来执行。
那么bootloader的剩余部分放在哪呢?一般来说是放在MBR与第一个分区之间的间隙处。
UEFI是指Unified Extensible Firmware Interface,可扩展固件接口。
等等,BIOS是基本输入输出系统,怎么到这就变成可扩展固件接口了,这说的是一个方面的东西吗?
当然是,实现了UEFI这个接口的固件称为UEFI固件,Legacy BIOS用于称呼之前使用的那种固件。
UEFI弱化了开机自检这个过程,使得PC启动更为迅速,所以UEFI固件的流程大概就是两步:初始化和装载bootloader。
UEFI比Legacy BIOS高级,BIOS加载bootloader的时候是不识别磁盘分区的,不管三七二十一把能引导的磁盘的MBR装内存中就完了。而UEFI能识别磁盘分区,当一个磁盘有可启动标志AA55H的时候,UEFI会尝试在这个分区中查找一个带有EFI System
标志的分区,也就是ESP(EFI System Partition),下面是一个U盘的示例。
➤ sudo fdisk -l /dev/sdb
Disk /dev/sdb: 14.6 GiB, 15707668480 bytes, 30679040 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 8F60A66B-D42D-4CC5-BC31-7C2CC9DB78B6
Device Start End Sectors Size Type
/dev/sdb1 2048 6293503 6291456 3G EFI System #这里,分区类型是EFI System
/dev/sdb2 6293504 6301695 8192 4M BIOS boot
/dev/sdb3 6301696 18884607 12582912 6G Microsoft basic data
如果找到了,UEFI会按照规定的路径/EFI/boot/bootx64.efi
加载并执行EFI application,其实EFI application就是bootloader。
EFI/
├── archiso
│ └── efiboot.img
├── boot
│ ├── bootx64.efi
│ └── bootx64fw.efi
├── MICROSOFT
│ └── BOOT
│ ├── BCD
│ └── BCD.LOG
└── ubuntu
└── grubx64.efi #ubuntu的bootloader
5 directories, 6 files
➤ file EFI/ubuntu/grubx64.efi
EFI/ubuntu/grubx64.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
既然bootloader是存放在一个单独分区中,而分区的大小可以任意调整,那么对于UEFI,bootloader就不必分成多段存储,相对与Legacy BIOS是一个进步。
新鲜事物出来的时候常常要考虑兼容旧事物,UEFI也是。
UEFI的分区表结构是GPT,GPT头存放在磁盘起始的第二个扇区(MBR在第一个),然后紧接着存放分区表,所以GPT的分区表并没有和MBR重叠,如果遇到不支持UEFI的机器,还是可以把MBR给加载到内存运行。顺便提一下,UEFI下面的MBR被称作PMBR。
可是,GPT占用了MBR之后的区域,这个区域之前是用来存放BIOS方式启动时的bootloader的。上面说过的,Legacy BIOS方式,MBR里面不是完整的bootloader,存放的只是bootloader的一部分,负责加载bootloader。既然这里不让放,为了兼容,那就放到别的地方去,放到一个单独的分区中,这个分区带有BIOS boot
标志。
Disk /dev/sdb: 14.6 GiB, 15707668480 bytes, 30679040 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 8F60A66B-D42D-4CC5-BC31-7C2CC9DB78B6
Device Start End Sectors Size Type
/dev/sdb1 2048 6293503 6291456 3G EFI System #这里有UEFI方式启动时的bootloader
/dev/sdb2 6293504 6301695 8192 4M BIOS boot #这里存放BIOS方式启动时的bootloader
/dev/sdb3 6301696 18884607 12582912 6G Microsoft basic data
需要注意的是,虽说BIOS boot是一个分区,但它并没有包含文件系统,建立文件系统只会加重MBR中那部分bootloader加载剩余部分bootloader的负担。
➤ sudo mount /dev/sdb2 /mnt
NTFS signature is missing.
Failed to mount '/dev/sdb2': Invalid argument
The device '/dev/sdb2' doesn't seem to have a valid NTFS.
Maybe the wrong device is used? Or the whole disk instead of a
partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?
所以不支持UEFI的机器还是能够通过兼容方式把bootloader载入的。
前面已经说过了bootloader最重要的任务就是,把操作系统给拉起来。
当然操作系统是存在与某个分区的,bootloader应该有识别磁盘分区的能力,现在的操作系统大都能识别MBR方式和GPT方式的分布表。
bootloader有很多种,操作系统也有很多种,这里不可能面面俱到,就说Linux下的grub吧。
grub引导Linux有两个关键的步骤:
下面是我电脑上grub配置(/boot/grub/grub.cfg
)中的两行,作用对应上面两个步骤
linux /boot/vmlinuz-4.15.0-65-generic root=UUID=0960cb68-1fdd-4ecc-a9c2-c031b6d0fffa ro text
initrd /boot/initrd.img-4.15.0-65-generic
这里打算写太多细节,一是我自己也不清楚grub的各种细节,二是如果真要写,都能单独拿出来写了。
上面介绍了PC启动的流程,重点介绍了固件阶段和bootloader阶段,从宏观上了解了引导的流程。