折腾引导(一):基础

文章目录

    • 简介
    • 启动流程
      • Legacy BIOS vs UEFI
        • Legacy BIOS
        • UEFI
          • 兼容Legacy BIOS
      • bootloader
    • 总结

简介

下面将介绍计算机启动的流程,涉及Legacy BIOS和UEFI两种启动方式,为后面继续折腾引导打下基础。

启动流程

从你按下电源键,到登录界面出现,期间发生了很多事,其中有一个叫bootloader的家伙就度过了它短暂的一生。

PC启动的过程可以分为三个阶段:

  1. 固件阶段:Legacy BIOS固件或者UEFI固件
  2. bootloader阶段:常见的bootloader有Windows的bootmgr,Linux的grub
  3. 操作系统启动阶段

固件负责初始化一些设备,检查设备是否正常,然后按照设定的顺序,依次查找可启动的磁盘。找到之后,把磁盘上的bootloader加载到内存,运行bootloader。

bootloader负责把操作系统这个大家伙给拉起来干活,bootloader是用户自定义的程序,所以他的功能可以多种多样,比如选择启动哪个操作系统。

操作系统启动阶段差别就大了,毕竟各家操作系统有各家的本事,这里就不展开来讲了。

下面详细介绍一下固件阶段和bootloader阶段

Legacy BIOS vs UEFI

这两个有什么区别?简单说就是Legacy BIOS是很久之前出现的,UEFI是之后出现的,有了很多新功能,设计也更科学,反正更高级就是了。

详细地说,UEFI支持下面的功能

  • 安全启动
  • 和它配套的分区表容量更大,分区灵活
  • 可使用图形界面
  • 从网络启动
  • 其他更多功能

Legacy BIOS

BIOS就是Basic Input/Output System,基本输入输出系统,之所以前面加了个Legacy,是因为UEFI出来之后这货就没什么地位,下面简称BIOS。

BIOS的流程是这样的:

  1. 开机自检(POST)
  2. 初始化
  3. 查找可启动盘
  4. 装载bootloader

这里假设读者已经了解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

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是一个进步。

兼容Legacy BIOS

新鲜事物出来的时候常常要考虑兼容旧事物,UEFI也是。

UEFI的分区表结构是GPT,GPT头存放在磁盘起始的第二个扇区(MBR在第一个),然后紧接着存放分区表,所以GPT的分区表并没有和MBR重叠,如果遇到不支持UEFI的机器,还是可以把MBR给加载到内存运行。顺便提一下,UEFI下面的MBR被称作PMBR。

折腾引导(一):基础_第1张图片

可是,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最重要的任务就是,把操作系统给拉起来。

当然操作系统是存在与某个分区的,bootloader应该有识别磁盘分区的能力,现在的操作系统大都能识别MBR方式和GPT方式的分布表。

bootloader有很多种,操作系统也有很多种,这里不可能面面俱到,就说Linux下的grub吧。

grub引导Linux有两个关键的步骤:

  1. 加载内核
  2. 加载initrd

下面是我电脑上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阶段,从宏观上了解了引导的流程。

你可能感兴趣的:(Linux)