这篇文章把几个经常被放在一起讨论、但实际上属于不同层次的概念串起来:固件、启动模式、分区方案、启动管理器、引导加载程序、ELF、Linux 内核和用户空间。
先给出一条总路线。这里使用 Blowfish 的 timeline,因为启动过程是有明确先后顺序的阶段链:
硬件复位与平台固件
1
BIOS / UEFI
CPU 从复位入口开始执行,固件初始化处理器、内存、总线和必要的启动设备。固件选择启动项
2
Boot Manager
Legacy BIOS 通常从磁盘首扇区开始执行;UEFI 通常根据启动项从 ESP 读取 EFI 程序。GRUB 或其他启动程序接管
3
Bootloader
启动程序读取配置,显示菜单,并准备要启动的操作系统或内核。Linux 内核开始运行
4
Kernel + initramfs
GRUB 把 Linux 内核、initramfs 和内核命令行交给内核启动协议,然后转移控制权。内核初始化系统
5
Memory / Drivers / Root FS
Linux 初始化内存、进程、驱动和文件系统,并通过 initramfs 找到和挂载真正的根文件系统。PID 1 与用户空间
6
systemd 或其他 init
PID 1 启动服务、登录界面、桌面和普通应用程序,系统进入正常运行状态。
这条路线的关键是:**启动模式和分区方案是两条不同的轴;ELF 是文件格式;GRUB 是启动程序;内核是被启动的操作系统核心。**把它们混成同一个概念,就很容易产生“UEFI 一定等于 GPT”“.efi 一定是 ELF”“内核就是一个普通 ELF 程序”等误解。
1. 先区分四个概念#
1.1 启动模式:Legacy BIOS 与 UEFI#
启动模式描述的是:计算机通电后,主板固件用什么方式寻找并运行启动程序。
- Legacy BIOS 是传统 PC 的启动接口。它通常读取磁盘的第一个扇区,把其中的代码放到约定的内存位置并开始执行。
- UEFI 是较新的固件接口。它可以访问 FAT 文件系统,读取 EFI 分区中的启动文件,并通过启动项选择要运行的 EFI 应用程序。
1.2 分区方案:MBR 与 GPT#
分区方案描述的是:磁盘如何记录分区的布局。
- MBR 分区表使用传统的分区表结构,分区数量和磁盘地址范围有限。
- GPT 使用 GUID 标识分区,并在磁盘头尾保存分区表信息和校验信息,适合现代磁盘。
MBR/GPT 本身不是启动模式,也不是启动加载程序。常见组合如下:
| 启动模式 | 分区方案 | 说明 |
|---|---|---|
| Legacy BIOS | MBR | 传统组合,BIOS 读取磁盘首扇区中的启动代码 |
| UEFI | GPT | 现代 PC 最常见组合,UEFI 从 ESP 读取 .efi 文件 |
| Legacy BIOS | GPT | 可以工作,但 GRUB 通常需要 BIOS Boot Partition |
| UEFI | MBR | 某些平台和介质可以工作,但不是现代 Linux 安装的首选组合 |
因此,“UEFI + GPT”和“BIOS + MBR”是常见的搭配,不是绝对的一一对应关系。
1.3 启动管理器与引导加载程序#
- **启动管理器(boot manager)**负责选择启动哪个系统、哪个内核或哪个启动项。
- **引导加载程序(bootloader)**负责把被选择的操作系统内核或下一级启动程序加载到内存,并把控制权交出去。
这两个角色经常由同一个程序承担。GRUB 就是典型例子:它可以显示菜单,因此充当启动管理器;它也可以加载 Linux 内核和 initramfs,因此充当引导加载程序。
UEFI 固件本身也具有启动管理功能:它可以根据 NVRAM 中的 BootOrder 和 Boot#### 启动项选择一个 EFI 文件。它选择并运行的文件可能是 GRUB,也可能是 Windows Boot Manager、systemd-boot 或其他 EFI 应用程序。
2. ELF 到底是什么#
ELF 是 Executable and Linkable Format 的缩写,即“可执行与可链接格式”。在 Linux/Unix 工具链中,ELF 可以表示:
- 可执行文件;
- 共享库,例如
libc.so; - 可重定位目标文件,例如编译器生成的
.o; - core dump;
- 通常情况下的 Linux 内核模块
.ko。
但 ELF 不是“所有 Linux 二进制文件的格式”。例如:
- UEFI 的
.efi启动程序通常是 PE/COFF 格式,不是 ELF; - Linux 的
vmlinuz通常是带有 Linux 启动协议的压缩内核镜像,不应简单地等同于普通 ELF 可执行文件; - 固件、设备微码和各种裸机镜像可能使用完全不同的格式。
2.1 ELF 文件的整体结构#
一个 ELF 文件通常包含:
ELF Header
├── Program Header Table(程序头表,可选但对可执行文件很重要)
├── Section Header Table(节头表,可选,链接和分析时常用)
└── 各种代码、数据、字符串和元数据ELF Header 中会记录一些基本信息,例如:
- 32 位还是 64 位;
- 目标机器架构,例如 x86-64、AArch64;
- 文件类型,例如可执行文件、共享对象或可重定位文件;
- 程序入口地址
e_entry; - 程序头表和节头表的位置。
e_entry 是一个入口地址,但它不等于“源代码第一行”,也不保证是文件中最先出现的机器指令。对于动态链接程序,入口还会经过运行时启动代码和动态加载器。
2.2 Section 与 Segment 不是一回事#
这是理解 ELF 时最容易混淆的一点。
**Section(节)**主要服务于编译、链接和调试,例如:
.text:机器指令;.rodata:只读数据和字符串常量;.data:已经初始化的可写数据;.bss:未初始化或初始化为零的数据,通常不需要在文件中保存完整内容;.symtab:符号表,可能在发布版中被删除;.debug_*:调试信息,通常不会保留在精简的发布版程序中。
**Segment(段)**主要服务于装载。操作系统装载 ELF 时,重点读取 Program Header Table 中的 PT_LOAD 等条目,把适合的文件区域映射到进程的虚拟地址空间,并设置读、写、执行权限。
所以更准确的说法是:
链接器把多个 Section 组织进若干 Segment;运行时加载器主要按照 Segment 装载程序,而不是逐个把
.text、.data当作独立文件块复制进内存。
可以使用下面的命令观察二者:
readelf -h ./program # ELF 总体信息
readelf -S ./program # Section 列表
readelf -l ./program # Program Header 和 Segment
readelf -d ./program # 动态链接信息
file ./program # 粗略识别文件类型2.3 ELF 程序是如何被 Linux 启动的#
以动态链接的普通程序为例:
shell 执行 ./program
↓
execve() 请求内核替换当前进程映像
↓
内核识别 ELF Header 和 Program Header
↓
内核映射可加载的 PT_LOAD 段
↓
根据 PT_INTERP 找到动态加载器(如果存在)
↓
动态加载器加载共享库并完成重定位
↓
进入程序入口和 C 运行时启动代码
↓
调用 main()这里的“映射”不一定意味着把整个文件一次性复制到 RAM。内核可以使用文件映射、按需调页和页缓存,让程序在真正访问页面时再读取数据。
2.4 .S 汇编文件与 ELF 的关系#
.S 是汇编源文件的一种常见后缀。大写 .S 通常表示它会先经过 C 预处理器;小写 .s 通常直接交给汇编器。它们都只是源代码,不是 ELF 文件。
典型编译链是:
boot.S / head.S
↓ 预处理器 + 汇编器
foo.o(通常是 ELF 可重定位目标文件)
↓ 链接器和链接脚本
可执行文件、内核镜像或共享库汇编源代码中的:
.section .text
.global _start
_start:
nop
.section .data
value:
.long 0x1234是在告诉汇编器如何生成输入 Section。之后链接器还会通过链接脚本决定这些 Section 在输出文件中的布局、虚拟地址和 Segment 归属。.S 中的 .text 与最终 ELF 中的 .text 有关系,但二者不是“同一个文件结构的文字版和二进制版”。
3. UEFI 模式下的启动流程#
下面以现代 x86-64 Linux、UEFI + GPT 为例。不同架构可能使用不同的内核启动协议,但整体层次相似。
3.1 CPU 复位,固件开始运行#
通电或复位后,CPU 从架构规定的复位入口开始执行主板固件。固件完成基本硬件初始化,并执行 POST,例如检查内存、处理器、显示设备和存储设备。
UEFI 不是一个完整的桌面操作系统。它是一个在操作系统之前运行的固件环境,提供设备访问、文件系统访问、启动服务和运行 EFI 程序所需的接口。
3.2 UEFI 启动管理器选择启动项#
UEFI 通常根据固件变量中的启动项选择程序。启动项可能指向:
\EFI\Microsoft\Boot\bootmgfw.efi
\EFI\ubuntu\grubx64.efi
\EFI\Linux\linuxx64.efi如果启动项丢失,某些场景下 UEFI 还会尝试可移动介质的默认路径,例如 x86-64 上常见的:
\EFI\BOOT\BOOTX64.EFI3.3 UEFI 读取 ESP 中的 EFI 程序#
ESP 是 EFI System Partition 的缩写。它通常使用 FAT 文件系统,用于存放 EFI 启动程序及其相关文件。ESP 是一个分区,不等于整个 /boot,也不等于 GPT 本身。
一个重要纠正是:
.efi文件通常是 UEFI 使用的 PE/COFF 镜像,而不是 ELF。
UEFI 规范定义了 PE32/PE32+ 形式的 EFI 镜像。GRUB 的 grubx64.efi 因此应当理解为一个 UEFI 可执行程序;它不是“加了 EFI 后缀的 ELF”。
3.4 GRUB 接管控制权#
UEFI 运行 grubx64.efi 后,GRUB 可能继续读取:
grub.cfg;- GRUB 模块;
- 字体、主题和本地化文件;
/boot中的 Linux 内核和 initramfs。
如果安装了多个系统或内核,GRUB 可以显示菜单。用户选择某个菜单项后,GRUB 通常会准备:
Linux 内核镜像
initramfs
内核命令行(root=、quiet 以及其他硬件参数等)这里的 initramfs 很重要。它是一个在真正根文件系统挂载前使用的临时根文件系统,常用于加载存储驱动、解锁 LUKS、组合 RAID/LVM、识别设备以及找到真正的根分区。
4. Legacy BIOS 模式下的启动流程#
下面以 BIOS + MBR 为例。
4.1 BIOS 读取磁盘第一个扇区#
BIOS 完成 POST 和设备选择后,会读取启动磁盘的第一个扇区。经典 BIOS 启动约定通常是:
- 将扇区读到物理地址
0x7C00附近; - 检查末尾的启动签名
0x55AA; - 把 CPU 控制权交给该扇区中的启动代码。
4.2 MBR 的 512 字节结构#
传统 MBR 启动扇区通常可概括为:
偏移 0 - 445:启动代码区域,常说的 446 字节
偏移 446 - 509:4 个 16 字节分区表项,共 64 字节
偏移 510 - 511:启动签名 0x55AA,共 2 字节这里要注意三件事:
- MBR 既可以指整个磁盘首扇区,也可以在上下文中指其中的分区表或启动代码。
- 分区表只是描述磁盘布局,不会自己加载操作系统。
- “MBR 中没有启动代码”只意味着无法通过传统 BIOS 从这个磁盘继续启动,不代表 UEFI 模式下所有启动方式都失效。
4.3 GRUB 在 BIOS 模式中的嵌入#
传统教学材料经常把 GRUB 描述成 Stage 1、Stage 1.5 和 Stage 2:
boot.img → 磁盘启动扇区中的最小代码
core.img → 嵌入区域中的核心镜像
模块和配置 → 文件系统中的 /boot/grub 等目录“Stage 1.5”有助于理解历史流程,但它不是所有 GRUB 版本和所有启动方式都存在的固定文件。现代 GRUB 2 文档更常使用 boot.img、core.img、模块和配置文件这些术语。
在 MBR 磁盘上,GRUB 的核心镜像通常尝试嵌入第一个分区之前的空间。这个空间经常被称为 MBR gap 或 embedding area,但它不是一个正式的文件系统分区,也不保证永远安全可用。现代分区工具通常会把第一个分区从约 1 MiB 处开始,以便对齐和给引导程序留下空间;GRUB 官方也建议预留足够的嵌入空间。
5. GPT、ESP 与 BIOS Boot Partition#
5.1 GPT 的基本布局#
GPT 磁盘通常包含:
LBA 0:Protective MBR(保护性 MBR)
LBA 1:主 GPT Header
后续:主 GPT 分区条目数组
中间:实际分区区域
磁盘末尾:备份分区条目数组和备份 GPT Header保护性 MBR 的主要目的,是避免只理解 MBR 的旧工具把 GPT 磁盘误认为“没有分区的空磁盘”并覆盖它。它不等于一套完整的 BIOS 启动代码。
GPT 的主表和备份表带有校验信息。磁盘头部的 GPT Header 会记录分区条目数组的位置、数量和大小;备份 GPT Header 位于磁盘末尾。
5.2 GPT + UEFI#
这是现代 PC 上最常见的组合:
UEFI
↓
GPT 磁盘上的 ESP
↓
ESP 中的 grubx64.efi 或其他 .efi 程序
↓
内核、initramfs、用户空间GPT 不直接“存放 GRUB”。更准确地说:GPT 提供分区布局,ESP 是其中一个使用 FAT 文件系统的分区,EFI 启动程序作为文件保存在 ESP 中。
5.3 GPT + BIOS#
GRUB 和 Linux 也可以在 GPT 磁盘上使用传统 BIOS 模式启动。由于 GPT 布局不应依赖一个随意的 MBR 后间隙,通常会创建一个特殊的 BIOS Boot Partition:
- 不需要文件系统;
- 不用于存放普通用户文件;
- 分区类型需要标记为 BIOS boot;
- 用于嵌入 GRUB 的核心镜像。
这个分区不是 ESP,也不是 /boot。它只服务于 BIOS 模式下的 GRUB 嵌入。
6. Linux 内核到底在哪里接手#
当 GRUB 准备好内核、initramfs 和命令行后,它会根据具体架构的启动协议跳转到内核入口。
以 x86 Linux 为例,/boot/vmlinuz-* 通常是经过压缩、带有启动协议结构的内核镜像;Linux 构建目录中的 vmlinux 通常是更接近链接结果的 ELF 文件。二者不能简单地互换:
vmlinux → 构建出的、常见为 ELF 的内核文件
vmlinuz → 安装到 /boot、可由启动程序使用的压缩/启动镜像
initramfs → 启动早期的临时根文件系统内核启动后,大致会完成以下工作:
- 建立内存管理和异常处理机制;
- 初始化调度器、时间系统和中断;
- 初始化内核子系统和设备模型;
- 初始化存储控制器、文件系统和网络等驱动;
- 解包或使用 initramfs;
- 找到并挂载真正的根文件系统;
- 通过
switch_root或类似机制切换离开 initramfs; - 启动 PID 1。
Linux 内核并不是“把所有硬件都直接交给固件管理”。固件和内核分工如下:
- 固件在操作系统之前初始化平台,并提供启动环境;
- 内核驱动负责运行时以操作系统的方式使用硬件;
- 某些设备还需要额外的设备固件,驱动会从系统中的固件目录加载它并传给设备;
- Linux 的设备固件目录常见为
/lib/firmware,在使用 usr-merge 的发行版中也可能位于/usr/lib/firmware。
6.1 用户空间启动#
PID 1 可以是 systemd、OpenRC、BusyBox init 或其他 init 实现。以 systemd 为例,它会继续:
- 启动基础服务;
- 挂载其他文件系统;
- 初始化网络;
- 启动日志、时间同步和设备管理服务;
- 启动显示管理器或文本登录服务。
到这里才进入我们通常理解的“登录界面和桌面”。因此,“启动完成”不是内核刚跳转时,而是用户空间逐步建立之后。
7. 启动链中的角色关系#
可以用下面的关系理解:
UEFI 固件的 Boot Manager
│ 选择一个 EFI 应用程序
▼
grubx64.efi / systemd-boot / Windows Boot Manager
│ 选择并加载内核或另一个启动程序
▼
Linux 内核 + initramfs
│ 初始化操作系统
▼
PID 1 和用户空间对于 BIOS:
BIOS
│ 读取并执行磁盘首扇区
▼
MBR 中的 boot.img
│ 加载 core.img
▼
GRUB 核心、模块和配置
│ 加载内核与 initramfs
▼
Linux 内核不要把这些角色强行理解为永远固定的“固件 → boot manager → bootloader → 内核”四个独立程序。实际情况可能是:
- UEFI 固件先选择 GRUB,而 GRUB 同时承担启动管理和内核加载;
- UEFI 固件直接选择 Linux 内核加载器,根本没有 GRUB;
- GRUB 通过 chainloading 把控制权交给 Windows Boot Manager;
- 一个启动管理器可以选择另一个启动程序,而不是直接加载内核。
8. 在正在运行的 Linux 中检查启动状态#
8.1 判断当前是 UEFI 还是 Legacy BIOS#
if [ -d /sys/firmware/efi ]; then
echo UEFI
else
echo Legacy-BIOS-or-unknown
fi这个判断应该在已经运行的 Linux 中执行。/sys/firmware/efi 通常表示内核是从 UEFI 路径启动的。
8.2 查看分区方案和文件系统#
lsblk -o NAME,SIZE,TYPE,FSTYPE,PARTTYPE,PARTLABEL,MOUNTPOINTS
sudo fdisk -l
sudo gdisk -l /dev/nvme0n1你可以重点观察:
- 是否存在
vfat的 ESP; - 是否存在 BIOS Boot Partition;
- 根文件系统、
/boot和 ESP 分别位于什么分区; - 磁盘是 GPT 还是 DOS/MBR 分区表。
8.3 查看 UEFI 启动项#
sudo efibootmgr -v8.4 查看内核、initramfs 和命令行#
ls -lh /boot/vmlinuz* /boot/initramfs* 2>/dev/null
cat /proc/cmdline
ps -p 1 -o pid,comm,args8.5 观察 ELF#
readelf -h /bin/ls
readelf -l /bin/ls
readelf -S /bin/ls这三个命令分别帮助你观察 ELF Header、Program Header/Segment 和 Section。
9. 几个需要纠正的常见说法#
| 容易误解的说法 | 更准确的说法 |
|---|---|
| UEFI 就是一个小型操作系统 | UEFI 是固件接口和运行环境,不等同于完整操作系统 |
| UEFI 一定使用 GPT | UEFI 常和 GPT 搭配,但启动模式和分区方案是不同概念 |
| GPT 没有 MBR | GPT 通常包含用于兼容和保护的 Protective MBR,但它不等于传统 MBR 启动方案 |
| GPT 不能用 BIOS 启动 | GPT 可以配合 BIOS 启动,GRUB 通常需要 BIOS Boot Partition |
.efi 是 ELF 加上后缀 | UEFI EFI 镜像通常是 PE/COFF 格式 |
ELF 的 .text、.data 就是加载器直接逐个复制的内存段 | ELF Section 和 Segment 不同,运行时主要按照 Program Header 中的 Segment 装载 |
| Linux 内核就是普通 ELF 程序 | vmlinux 常见为 ELF;vmlinuz 是可由启动协议使用的内核镜像,启动方式更特殊 |
| Stage 1.5 永远是 GRUB 的一个文件 | 这是对传统 GRUB 启动过程的教学化称呼,现代 GRUB 2 更适合用 boot.img、core.img 和模块描述 |
| MBR 中没有代码就所有系统都无法启动 | 只有依赖传统 BIOS 从该磁盘启动的路径会失败,UEFI 路径可以使用 ESP |
| 固件和驱动程序是一回事 | 固件通常运行在设备或平台中;驱动是内核用来控制设备的软件接口 |


