Appearance
操作系统引导
2026 大纲 一(五)操作系统引导。
运行程序需要操作系统,那操作系统自己是谁运行的
到这里,OS 的权威、求助的通道、程序装进内存的过程都讲完了。现在回到最开头: 这套东西自己是怎么起来的?
这是个真正的循环。运行一个程序需要操作系统——它要把程序从磁盘读进内存、 要建立页表、要分配 PCB。可操作系统自己也是一个躺在磁盘上的程序。 谁把它读进内存?
打破循环的办法叫自举(bootstrap)——这个词本来的意思是"揪着自己的鞋带把自己提起来"。 做法是让一段位置固定、断电不丢的代码先跑起来,由它读进下一段稍大一点的代码, 一级一级把系统提上来。
关键在于每一级怎么找到下一级。正常情况下"找一个程序"要靠文件系统按路径解析, 可引导的时候文件系统还没有。所以引导链上的每一步都在做同一件事: 用一个位置约定,顶替一层还不存在的抽象。这条线索贯穿全节, 把它抓住,MBR、PBR、UEFI 三者的关系就是一句话的事。
一、自举:把"鸡生蛋"的循环打断
最初那段代码必须同时满足两个条件:位置固定——CPU 加电复位后 PC 被硬件置成一个固定的物理地址,它只会从这里取第一条指令;断电不丢——所以必须放在 ROM / Flash ROM 这类非易失存储里,不能放在断电即失的内存里,更不能放在需要驱动程序才能读的磁盘上。于是第一棒必然是固化在 ROM 中的固件(BIOS/UEFI)。
ROM 容量有限且出厂后基本不可改,把"识别文件系统、找到内核文件、加载它"全塞进 ROM 是不现实的——换一种文件系统就得换主板。所以自举装入程序被拆成两段:
| 段 | 存放位置 | 职责 | 为什么放这里 |
|---|---|---|---|
| 第一段:ROM 中的固件 | ROM / Flash ROM,不可改或极少改 | 自检硬件,从磁盘的固定位置(第 0 扇区)读一小段代码进内存并跳过去 | 它必须存在、必须断电后仍在,所以只能固化;也因此只能做与具体 OS 无关的事 |
| 第二段:磁盘上的引导程序 | 磁盘第 0 扇区(MBR)及活动分区首扇区(PBR) | 认识分区表、认识文件系统、找到并加载内核映像 | 它可随 OS 一起更新;换系统只需重写磁盘上的这几个扇区,主板不用动 |
分界原则一句话:固件只负责"从一个约定死的位置读一段代码并交出控制权",一切与具体操作系统相关的知识都放在磁盘一侧。
二、引导链路:每一级在哪里执行
每一级交出控制权的依据不同,这一列才是这条链的骨架:
| 交接 | 依据是什么 | 若依据不成立会怎样 |
|---|---|---|
| 硬件 → 固件 | 固定的复位地址,由 CPU 硬件规定 | 无——这是硬连线的,不存在"找不到" |
| 固件 → MBR | 约定的固定位置:引导设备的第 0 扇区,且末尾两字节为 0x55AA | 无 0x55AA 标志则认为该盘不可引导,换下一个引导设备 |
| MBR → PBR | 分区表中被标记为"活动"的那一项给出的起始扇区号 | 无活动分区 / 多个活动分区 → 报错停机 |
| PBR → 内核 | 本分区文件系统的元数据(见第四节) | 内核文件缺失或被移动 → 报"找不到操作系统" |
| 内核 → 用户进程 | 内核主动创建第一个用户进程 | 创建失败则系统 panic |
三、MBR:512 字节是怎么被分掉的
三个数不是并列摆着的,它们之间有一条推导链:
分区表的每个表项要记录:该分区是否活动、起止柱面/磁头/扇区、分区类型、起始扇区号(LBA)、总扇区数——这些字段合计固定占 16 B,于是
把分区表项从 16 B 改成 32 B,这条推导链还能不能自己走(想确认"最多 4 个主分区"不是一个孤立常数时展开)
设扇区仍为 512 B、结束标志仍为 2 B,但每个分区表项扩到 32 B,引导代码至少保留 446 B。
第 1 步:算留给分区表的空间。 一个扇区的总量是硬约束,三段是完全瓜分关系,先把不可动的两段扣掉:
第 2 步:用表项大小去除。 分区表是定长表项的数组,能放几项完全由"总空间 ÷ 单项大小"决定:
第 3 步:反过来求引导代码的上限。 同一个瓜分等式换个未知量:要放下 4 项 × 32 B = 128 B 的分区表,引导代码最多
四、PBR 怎么在"还没有文件系统"的时候找到内核
内核在磁盘上是一个普通文件(如 /boot/vmlinuz、ntoskrnl.exe)。可"文件"是文件系统提供的抽象——按路径找到一个文件需要已经运行起来的文件系统代码去解析目录、读 inode/FAT 表、追踪数据块,而此刻根本没有任何文件系统模块在运行。
PBR 的做法是自带一小段"够用就行"的读盘代码:分区被格式化成哪种文件系统在格式化时就已确定,格式化程序在写 PBR 的同时写进去的就是与该文件系统配套的那一版引导代码;分区首部还保存着该文件系统的元数据(如 FAT 的 BPB:每扇区字节数、每簇扇区数、FAT 表位置与份数、根目录位置),PBR 读这几个字段即可算出根目录在哪个扇区,然后按扇区号硬读、逐条比对文件名。
核心思路:不是"先有文件系统再找文件",而是把定位内核所需的最少信息固定在磁盘的约定位置上,用几十行代码硬读出来。 引导阶段的每一级都在做同一件事——用一个位置约定替代一层还不存在的抽象:
| 层级 | 缺的抽象 | 用什么位置约定顶上 |
|---|---|---|
| 固件找引导程序 | 还没有分区的概念 | 第 0 扇区 + 0x55AA |
| MBR 找 PBR | 还没有文件系统 | 分区表里的"活动"标志 + 起始扇区号 |
| PBR 找内核 | 还没有目录/路径解析 | 分区首部的文件系统元数据 + 硬编码的根目录扫描 |
现代系统通常在 PBR 与内核之间再插一级引导管理器(如 GRUB),它有完整的文件系统驱动、能显示菜单、能引导多个操作系统——但它自己仍然是被上面这套"位置约定"机制搬进内存的。
五、UEFI
| 维度 | BIOS + MBR | UEFI + GPT | 判据/后果 |
|---|---|---|---|
| 固件存放 | ROM | Flash ROM,可在线更新 | 后者能随硬件演进升级 |
| 分区方案 | MBR(分区表 64 B,最多 4 个主分区) | GPT,分区数与容量上限大幅提高 | MBR 的 4 项上限与 32 位起始扇区号是硬限制 |
| 引导代码在哪 | MBR 第 0 扇区的 446 B 里 | ESP(EFI 系统分区)上的 .efi 可执行文件 | 前者受 446 B 死限,后者是普通文件,大小几乎不受限 |
| 固件要不要懂文件系统 | 不懂,只会按扇区号读 | 懂——固件内置 FAT 驱动,能直接按路径打开文件 | 这是最关键的一条差别 |
| 引导多系统 | 需要额外的引导管理器改写 MBR | 固件里维护启动项列表,各系统各放自己的 .efi | 后者互不覆盖 |
ESP 是一个格式化为 FAT 的专用分区,各操作系统把自己的引导程序以 .efi 文件的形式放在里面(如 \EFI\Microsoft\Boot\bootmgfw.efi)。UEFI 的引导过程因此变成:加电 → 固件自检 → 固件用内置 FAT 驱动读 ESP → 按启动项加载指定的 .efi → 由它加载内核,MBR 那 446 B 引导代码这一环被彻底跳过。回头看第四节那张表:UEFI 等于把"固件不懂文件系统"这个前提取消掉了,于是"用位置约定顶替抽象"的那一层也就不再需要。
六、内核初始化与第一个用户进程
内核拿到控制权后分三段:① 建立自己的运行环境——初始化中断向量表、建立内核页表并开启分页、设置内核栈,做完这一步中断才可用;② 建立管理数据结构——初始化进程管理、内存管理(空闲页框表)、文件系统与设备管理的各种表,探测并加载设备驱动;③ 造出第一个进程。
第 ③ 步同样是一个"鸡生蛋"问题:进程由父进程创建,那第一个进程的父进程是谁?答案是没有——内核直接分配一个 PCB、填好初始值、把内核初始化代码所在的执行流认领为一个进程,这就是 0 号进程。它随后退化成闲逛进程,在就绪队列为空时占住 CPU(详见调度的实现)。由它创建的 1 号进程 init 才是第一个用户进程,负责启动系统服务、打开终端、把用户带到登录界面。此后一切都靠 fork/exec 自我繁衍,进入进程创建与控制的正常轨道。
七、这块盘是怎么变成启动盘的
前六节讲的是"开机时按这条链跑下来",前提是磁盘上那些东西早就在那儿了。 它们是什么时候写进去的?顺序同样不能颠倒,而且这个顺序和引导顺序是两回事。
把一块新盘做成启动盘要四步:
| 顺序 | 操作 | 别称 | 这一步的产出 | 为什么不能提前 |
|---|---|---|---|---|
| ① | 物理格式化 | 低级格式化 | 在磁道上划分扇区、安排扇区排列、标记坏道 | 没有扇区根本无法存任何数据。一般出厂已完成 |
| ② | 磁盘分区 | — | 划出 C、D、E 等逻辑分区,写入 MBR 与分区表 | 分区表要写进第 0 扇区,得先有扇区 |
| ③ | 逻辑格式化 | 高级格式化 | 建立引导记录(PBR)、FAT / inode 表、根目录、数据区 | 逻辑格式化是针对单个分区做的,没分区就无从下手 |
| ④ | 操作系统安装 | — | 把内核等文件写进分区,配置引导项 | 安装程序要往已建好的文件系统里写文件 |
一个东西属于哪一步,看它是谁的概念
这四步的产出很容易记混,判据其实只有一条:这个东西是硬件层面的、分区层面的, 还是文件系统层面的。
- 扇区是磁盘硬件的概念 → 物理格式化。
- MBR、分区表是整块盘的概念 → 分区。
- 根目录、FAT 表、inode 表、PBR 都是文件系统的概念 → 逻辑格式化。
所以"根目录在物理格式化时建立"是错的——根目录跟扇区完全不在一个层次上。
还要再分一次:哪些是每次开机现建的
上面四样都是一次写好、断电仍在的,写在磁盘上。但内核初始化时还会建一批东西, 它们只存在于内存里,每次开机都要重建,最典型的就是中断向量表。
判据很直接:东西在磁盘上就只建一次,在内存里就每次开机都得建。
| 东西 | 什么时候建 | 建在哪 |
|---|---|---|
| 中断向量表 | 每次开机,内核初始化时 | 内存 |
| 硬盘分区表 | 分区时,一次 | 磁盘第 0 扇区 |
| 文件系统根目录 | 逻辑格式化时,一次 | 磁盘 |
| 文件系统索引节点表 | 逻辑格式化时,一次 | 磁盘 |
中断向量表必须在初始化阶段就建好,否则后续任何一次中断都会失去归宿—— 而中断是 OS 的驱动力,它一失效,整个系统就再也动不起来了。
考点速记
- 自举靠一段位置固定、断电不丢的 ROM 固件打断循环;引导程序做成两段式,固件只做与 OS 无关的事,磁盘一侧承载所有与具体 OS 相关的知识。
- 引导顺序是 ROM 中的引导程序 → 磁盘引导程序(MBR)→ 分区引导程序(PBR)→ 操作系统初始化程序。最先执行的是 ROM 里的固件,且就在 ROM 里执行;MBR 起所有代码都在内存中运行,操作系统最终被加载到 RAM。
- MBR 找分区,PBR 找内核。判据是它在整块盘上还是在某个分区里。MBR 只需看懂分区表,PBR 必须看懂一种文件系统。
- MBR 的 512 B 是算出来的:引导代码 446 B + 分区表 64 B + 结束标志 0x55AA 2 B,
——"最多 4 个主分区"是推出来的,不是规定的常数。 - 通用套路:用"位置约定"顶替尚不存在的抽象(第 0 扇区顶替分区、活动标志顶替目录、分区元数据顶替路径解析)。UEFI 让固件看得懂文件系统,这一层就不再需要——这才是 BIOS 与 UEFI 的根本差别,"支持大硬盘"只是结果。
- 制作启动盘的顺序是物理格式化 → 分区 → 逻辑格式化 → 装 OS。扇区在物理格式化时划,根目录、FAT/inode 表在逻辑格式化时建,分区表在分区时写。
- 在磁盘上的只建一次,在内存里的每次开机都要建。中断向量表属后者。
- 内核初始化末尾手工构造 0 号进程(它没有父进程,是被追认的),随后退化为闲逛进程;它创建的 1 号进程 init 才是第一个用户进程。
这一节在真题里被考过的形式:
只有三道题,但其中一道是 8 分大题,且问的全是顺序——本节所有考法都可以归结成 "把几件事排对次序",而次序一共就两条:引导时的执行顺序和制盘时的操作顺序。
- 问操作系统最终被加载到哪里(2013-29)。答 RAM。⚠️ 干扰项是 ROM 和 EPROM——固件在 ROM 里没错,但 OS 是从磁盘读进内存执行的,"最终"两个字指的就是它的落脚点。
- 问四个引导程序的执行顺序、制作启动盘四个操作的顺序、以及扇区划分与根目录建立各在哪一步(2021-46,三问全在本节)。三问依次答:ROM 引导程序 → 磁盘引导程序 → 分区引导程序 → OS 初始化程序;物理格式化 → 分区 → 逻辑格式化 → 装 OS;扇区划分在物理格式化,根目录建立在逻辑格式化。⚠️ 两条顺序容易互相干扰——引导时是"从整盘往分区里走",制盘时是"从物理往逻辑往上盖",方向恰好一致但对象不同。
- 问哪样东西需要在操作系统初始化过程中创建(2022-24)。答中断向量表。⚠️ 另三项(分区表、根目录、索引节点表)都是磁盘上持久存在的,制盘时就建好了,开机时不需要也不应该重建。这道题考的就是第七条那个"在内存还是在磁盘"的判据。
复习优先级:必须拿满,且要按大题的标准准备——2021-46 说明这一节能出到 8 分。 两条顺序、四步产出、"内存 vs 磁盘"这条判据,是全部得分点。 MBR 那 512 B 的拆分和 UEFI 的差别至今只在选择题层面考过一次,理解即可,不必背 ESP 的路径。
易错:认为最先执行的是磁盘上的引导程序。最先执行的是 ROM 里的固件,且它就在 ROM 里执行。
易错:认为操作系统最终被加载到 ROM。ROM 里的是固件;OS 被读进 RAM 执行。
易错:把根目录的建立放在物理格式化。根目录是文件系统的概念,在逻辑格式化时建立;物理格式化只划扇区。
易错:认为中断向量表在磁盘上、开机时不用建。它在内存里,每次开机由内核初始化时重建。
易错:把"最多 4 个主分区"当成一个规定的常数。它是
算出来的。
易错:认为 BIOS 与 UEFI 的根本差别是支持的硬盘容量。根子是固件懂不懂文件系统,容量上限只是随之而来的结果。
易错:认为 0 号进程是第一个用户进程。0 号进程是内核手工构造的,退化为闲逛进程;它的子进程 init(1 号)才是第一个用户进程。