从 BIOS 到 GRUB:硬盘开头的 512 字节,怎样叫醒 Linux?
沿着老式台式机的启动路线,看看 BIOS 怎样读硬盘、MBR 为什么这么小,以及 GRUB 如何一步一步找到 Linux。
上一篇我们把电脑开机看成一场接力:固件先醒来,启动程序再接手,最后把 Linux 内核请进内存。现在我们把镜头拉近,专门看一条老式台式机路线:传统 BIOS 怎样从硬盘最前面的一个小扇区出发,最后把 Linux 交给内核。
先想象一个仓库。管理员到了仓库门口,手里只有一张很短的便条。便条上写着:去哪个货架找下一张更详细的地图。管理员找到地图后,才能继续找工具箱和负责人。传统 BIOS 启动大致如此:BIOS 先找到硬盘开头的 MBR,MBR 再想办法找到更完整的启动程序 GRUB,GRUB 最后找到 Linux 内核。
先认清三个容易混淆的名字
BIOS 是主板上的固件,可以把它想成最早上班的管理员。它负责做最基本的开机准备,并按启动顺序尝试硬盘、U 盘或其他设备。
MBR 是硬盘最前面一小块区域的常见叫法。它的全名是主引导记录。传统 MBR 里既有一点点启动代码,也有一张记录硬盘分区的表。它是硬盘上的内容,不是主板上的程序。
GRUB 则是一个真正的启动程序。它能理解更多磁盘和文件系统信息,可以显示启动菜单,找到 Linux 内核和 initramfs,再把它们送进内存。它比 MBR 里的那几行小程序本领大得多。
BIOS 在主板上;MBR 在硬盘开头;GRUB 是被 BIOS 和 MBR 一棒一棒叫醒的启动程序。
第一棒:BIOS 选定一块启动硬盘
电脑通电后,BIOS 会先进行基本检查,并准备好一部分硬件。之后它会按照设置好的启动顺序找设备:也许先看 USB 接口有没有 U 盘,再看 SATA 硬盘,或者尝试从网络启动。这个顺序可以在主板设置里调整。
假设 BIOS 选中了台式机里的 SATA 硬盘。它需要读硬盘上的内容,于是使用自己提供的早期磁盘读写服务。可以把它想成仓库里的老式传呼台:当时的启动代码还不认识各种现代文件系统,就先请固件帮忙把指定位置的一小段内容取出来。
在传统路线里,BIOS 通常会读取硬盘最前面的第一个扇区。扇区是硬盘一次读写的一小块空间;传统 PC 的这个启动位置通常按 512 字节来理解。把它放在今天看,512 字节比一条短短信还小,却要承担启动接力的第一棒。
BIOS 会把读到的内容放进内存的一个约定位置,然后让处理器从那里开始执行。资料里常会出现地址 0x7C00。你可以把它当成双方约好的柜台号码:BIOS 说“我把第一段程序放在这里”,接下来的程序就从这个位置开始工作。
MBR 里的 512 字节究竟装了什么?
传统 MBR 扇区可以粗略分成三部分:前面的 446 字节放启动代码,中间的 64 字节放分区表,最后 2 字节放一个约定的标记。三部分加起来正好 512 字节。
MBR 扇区示意图一个传统 MBR 扇区:512 字节
┌──────────────────────────────┐
│ 启动代码:446 字节 │ 负责寻找下一棒,空间很紧张
├──────────────────────────────┤
│ 分区表:64 字节 │ 4 个记录,每个 16 字节
├──────────────────────────────┤
│ 结束标记:2 字节 │ 常见写法是 55 AA
└──────────────────────────────┘先看分区表。分区就是把一整块硬盘划成几个互相区分的区域,例如一个放系统,另一个放个人文件。传统 MBR 的分区表只有四条记录,每条 16 字节。古早年代,硬盘容量小、系统也简单,这种安排曾经够用;后来人们需要更大的硬盘和更多分区,就发展出了 GPT 等新方式。
最后两个字节常写作 0x55、0xAA,是一个传统启动扇区标记。它有点像信封上的格式印章,让软件知道这里看起来像一个可启动扇区。具体固件对它的检查方式会有差异,所以它不是神奇的“有它就一定能启动”按钮。
这样一来,真正可以拿来放启动代码的地方最多只有 446 字节。还要在这几百个字节里写出程序,读硬盘,找到下一段代码,并把必要信息交过去。想把整套 Linux、文件系统支持和漂亮菜单都塞进去,当然远远不够。
MBR 的工作:只负责把人领到下一处
最简单的一类 MBR 程序会查看分区表,寻找标记为活动分区的那一项,然后读取这个分区开头的启动扇区,再把控制权交给它。你可以把分区表想成仓库的货架目录,而活动分区就是目录里贴着“从这里开始”的标签。
不过电脑上安装了 GRUB 时,情况可能不同:安装程序会把 GRUB 的第一小段代码放进 MBR 的启动代码区域。于是,BIOS 把 MBR 交给处理器后,这一小段 GRUB 代码就开始找 GRUB 的下一部分。不同系统、不同安装方式写入 MBR 的内容并不完全一样。
无论是谁写进去的,这一小段程序都面临同一个难题:它只有几百字节,而且一开始通常还不会读懂 ext4、Btrfs 这样的文件系统。它不能像我们打开文件夹一样,直接说“请打开 /boot/grub 里的某个文件”。
GRUB 为什么要分成好几小段?
GRUB 把接力棒拆成几段,就是为了绕过 MBR 空间太小的问题。第一段极小,任务只是找到磁盘上一个事先安排好的位置,读取第二段。第二段代码比较完整,可以把 GRUB 的核心部分装进内存。
GRUB 手册把 BIOS 启动时的第一段叫作 boot.img。在 PC BIOS 启动里,它的大小正好是一个启动扇区。它不负责理解文件系统,只记着 GRUB 核心映像第一块内容在磁盘的什么位置;安装 GRUB 时,安装程序会把这个位置记进去。
这里容易和刚才的 446 字节打架:boot.img 这个映像文件按一个完整扇区来制作,大小是 512 字节;但传统 MBR 还要保存分区表,所以真正可用来放 MBR 启动代码的空间仍然只有前面的 446 字节。安装程序会按启动扇区的布局写入需要的内容,分区表仍要留在自己的位置。
接下来常见的是 diskboot.img,它先把 core.img 的其余部分读入内存。core.img 可以把更多功能带进来,例如理解文件系统、显示 GRUB 菜单、读取配置文件。具体用了哪些小模块,要看这台电脑的安装方式和需要。
这里有个重要限制:最早的代码还没学会读文件夹时,只能按安装时记录的磁盘位置去拿下一段。可以把它想成快递员手里只有一张写着“第 200 个货箱”的纸条;等更完整的 GRUB 到场,他才有能力看货架标签、按文件名找东西。
core.img 藏在哪里?
在使用传统 MBR 分区表的硬盘上,GRUB 经常把 core.img 放在 MBR 后面、第一分区前面留下的一段空隙里。这段地方常被叫作 MBR gap,也就是 MBR 间隙。它不属于普通文件夹,因此早期启动代码可以直接按磁盘位置去读它。
以前有些分区工具只留下很小的空隙。随着文件系统、磁盘阵列和 AHCI 等存储方式变得复杂,GRUB 需要带更多代码,太小的空隙就不够用了。现在的分区工具通常会留下更大的空间,但具体布局仍取决于硬盘、工具和安装年代。
如果硬盘使用 GPT 分区表,却还要走传统 BIOS 启动,GRUB 可能需要一块专门留给自己的小分区,称为 BIOS Boot Partition(BIOS 启动分区)。它不是存放照片和文档的普通分区,而是给 GRUB 安放启动核心映像的空地。
所以,“用了 GPT 就不能用 BIOS”并不准确。GPT 常和 UEFI 一起出现,但也能配合传统 BIOS;只是 GRUB 在这种组合下需要合适的地方安放自己的核心映像。
GRUB 找到配置和菜单
core.img 启动后,GRUB 已经有能力认识更多磁盘结构和文件系统。它会寻找自己的配置文件,常见位置是 /boot/grub/grub.cfg。配置文件会告诉它有哪些系统可启动、菜单要怎样显示,以及内核和 initramfs 文件放在哪里。
你在开机时看到的 Linux、恢复模式或其他系统菜单,通常就是 GRUB 按照配置文件显示出来的。菜单倒计时结束后,它会选择一个条目;如果只有一个系统,也可能很快就过去,让人几乎看不到菜单。
GRUB 不一定必须从 MBR gap 里取 core.img。其他安装方式可以把它放在文件系统里,再记录它占用的磁盘位置。究竟选哪种布局由安装工具和磁盘情况决定,因此同一台电脑重装系统前后,硬盘上的具体安排也可能不一样。
终于轮到 Linux:GRUB 把内核交出去
用户选中 Linux 后,GRUB 会把 Linux 内核文件从硬盘读进内存。很多桌面系统还会同时读入 initramfs。它像一个临时工具箱,里面可能有识别硬盘控制器、加载文件系统驱动、解锁加密磁盘所需的小程序。
GRUB 还会准备一些启动参数,告诉内核该去哪里找正式系统。文件都在合适位置、基本信息也交代好之后,GRUB 才会跳到内核的入口,让 Linux 开始接手。这个时刻之后,开机的主角换成了内核。
内核会继续认识硬件、准备自己的内存管理方式,并找到真正的根文件系统。之后它启动第一个用户程序,也就是 PID 1。那一段我们已经在上一篇的大路线图里见过;后续还会专门讲设备树如何把硬件说明交给内核,以及 init 怎样把系统服务逐个带起来。
把传统台式机这条路线重新走一遍
传统 BIOS + GRUB + Linux按下电源
↓
BIOS 做基本准备,挑选启动硬盘
↓
读取硬盘第一个扇区(传统 MBR)
↓
MBR 里的小代码找到 GRUB 的下一段
↓
GRUB 装入 core.img,读取配置并显示菜单
↓
GRUB 把 Linux 内核和 initramfs 放进内存
↓
Linux 内核开始工作,随后启动 PID 1这条路线里,MBR 不是 Linux,GRUB 也不是 BIOS。它们分别在自己的位置上接力:固件读出第一小段,第一小段找更完整的启动程序,更完整的启动程序再找内核。电脑不是一下子从关机跳到桌面,而是一步一步把能力传下去。
如果启动失败,可能停在哪一棒?
如果屏幕显示找不到启动设备,问题可能发生在 BIOS 没有找到可启动设备这一步,也可能是启动盘、启动模式或磁盘连接出了问题。这个提示只能告诉我们接力没有顺利开始,不能单凭一句话断定是哪一个零件坏了。
如果看到 GRUB 菜单,说明 BIOS 和至少一部分 GRUB 已经成功工作。若进入 GRUB rescue 一类的提示,常见方向是 GRUB 找不到自己的配置、模块或系统文件。看到 Linux 内核报错,则意味着接力棒已经传得更远,排查重点也要转向内核、驱动、initramfs 或根文件系统。
把故障画面对应到启动的哪一步,就像顺着接力赛看是哪一棒停下来了。它能帮我们缩小范围,但仍要结合错误内容、硬盘布局和系统设置来判断。
BIOS 先读取一扇区;MBR 的空间只够写一张小便条;GRUB 接过来后,才有本领按文件名寻找 Linux。
如今的新电脑大多走 UEFI 路线:固件可以从 EFI 系统分区里按文件名打开启动程序,不必沿用“BIOS 读 MBR、再找 GRUB”的同一套步骤。下一篇我们再专门比较传统 BIOS 启动、UEFI 启动和兼容模式 CSM,看看它们为什么会同时出现在主板设置里。