2. Linux 引导流程:从 BIOS/UEFI 到内核启动的完整流程
Linux 系统的启动是一个分阶段、高度结构化的过程,从硬件固件初始化开始,经过引导加载程序,最终将控制权移交给内核。理解这一完整流程是分析 ACPI 电源状态初始化故障的前提。
2.1 固件阶段:BIOS 与 UEFI
系统上电后,CPU 首先执行固件代码。当前主流固件接口分为传统 BIOS 和现代 UEFI,二者在引导方式上存在本质差异。
| 特性 | 传统 BIOS | UEFI |
|---|---|---|
| 运行模式 | 16 位实模式 | 32/64 位保护模式或长模式 |
| 分区表 | MBR(主引导记录) | GPT(GUID 分区表) |
| 引导设备扫描 | 扫描第一个扇区(512 字节) | 读取 EFI 系统分区(ESP)中的 .efi 文件 |
| ACPI 支持 | RSDP 通常位于 EBDA 或 0xE0000-0xFFFFF | RSDP 通过 UEFI 配置表直接传递 |
| 安全启动 | 不支持 | 支持(Secure Boot) |
关键点:UEFI 固件在启动服务阶段会维护 ACPI 表(RSDT/XSDT)的指针,内核可以直接通过 UEFI 配置表获取,无需像 BIOS 那样在内存中扫描 RSDP 签名 "RSD PTR"。
2.2 引导加载程序阶段:GRUB 的作用
GRUB(GRand Unified Bootloader)是 Linux 生态中最常用的引导加载程序。它在固件与内核之间扮演“桥梁”角色,负责加载内核映像、初始 RAM 磁盘(initrd)以及传递引导参数。
GRUB 的核心工作流程
- 加载自身核心映像:从引导设备读取 core.img(BIOS)或 grubx64.efi(UEFI)。
- 解析配置文件:读取
/boot/grub/grub.cfg,显示菜单选项。 - 加载内核与 initrd:根据菜单项将 vmlinuz 和 initramfs 映像读入内存。
- 设置引导参数:构造
linux命令行(如acpi=off、pci=noacpi)。 - 启动内核:通过
boot命令将控制权交给内核入口点。
调试提示:在 GRUB 菜单按
e 键可以编辑引导参数。临时添加 acpi=debug 可以开启 ACPI 早期调试输出,有助于定位电源状态初始化阶段的故障。
2.3 内核解压与早期初始化
当 GRUB 执行 boot 命令后,控制权转移到内核的入口点。对于 bzImage 格式的内核,入口位于 arch/x86/boot/header.S 中的 start_of_setup。
内核解压阶段(实模式)
- setup.bin:运行在实模式,执行硬件探测、内存大小检测、视频模式设置。
- 切换到保护模式:通过
ljmp指令跳转到 32 位代码段。 - 解压内核:调用
decompress_kernel(),将压缩的内核映像(vmlinux.bin.gz)解压到高地址内存。
内核早期初始化(保护模式/长模式)
解压完成后,内核进入 start_kernel() 函数,这是体系结构无关的 C 语言入口。关键初始化步骤包括:
| 顺序 | 函数/模块 | 说明 |
|---|---|---|
| 1 | sched_init() |
初始化进程调度器 |
| 2 | build_all_zonelists() |
建立内存管理区列表 |
| 3 | page_alloc_init() |
初始化页分配器 |
| 4 | trap_init() |
设置中断描述符表(IDT) |
| 5 | acpi_boot_table_init() |
ACPI 早期初始化:定位 RSDP 并验证签名 |
| 6 | acpi_early_init() |
解析 RSDT/XSDT,注册表地址 |
| 7 | acpi_subsystem_init() |
初始化 ACPI 子系统,加载 DSDT |
故障高发区:ACPI 早期初始化阶段(步骤 5-7)是电源状态初始化故障的集中爆发点。常见问题包括:RSDP 指针损坏、DSDT 表校验和错误、AML 解释器解析失败。如果此阶段失败,内核通常会回退到
acpi=off 模式,导致电源管理功能完全丧失。
2.4 引导流程中的 ACPI 表传递机制
ACPI 表从固件到内核的传递路径决定了电源状态信息的完整性。内核通过以下方式定位 ACPI 根系统描述指针(RSDP):
- UEFI 系统:通过 UEFI 配置表(Configuration Table)中的
ACPI_TABLE_GUID直接获取 RSDP 地址。 - BIOS 系统:在扩展 BIOS 数据区(EBDA)或系统内存 0xE0000-0xFFFFF 范围内搜索 "RSD PTR" 签名。
- 引导加载程序传递:GRUB 可以通过
linux命令的acpi_rsdp=参数手动指定 RSDP 地址。
故障排查线索:如果内核启动日志中出现
1. 固件设置中是否禁用了 ACPI。
2. 引导参数中是否误加了
3. UEFI 固件版本是否需要更新以修复 ACPI 表损坏问题。
ACPI: Unable to locate RSDP 或 ACPI: Core revision 20200925 后无后续 ACPI 输出,说明 ACPI 表传递失败。此时应检查:1. 固件设置中是否禁用了 ACPI。
2. 引导参数中是否误加了
acpi=off 或 acpi=noirq。3. UEFI 固件版本是否需要更新以修复 ACPI 表损坏问题。
2.5 引导阶段 ACPI 电源状态初始化关键点
在 acpi_subsystem_init() 完成后,内核已经具备读取和解析 ACPI 表的能力。电源状态初始化(C-states、P-states、T-states)依赖于以下 ACPI 表的正确加载:
| ACPI 表 | 电源状态相关 | 初始化时机 |
|---|---|---|
| DSDT | 定义 _CST、_PCT、_PPC 等控制方法 | acpi_subsystem_init() |
| SSDT | 处理器和设备特定的电源状态覆盖 | acpi_load_tables() |
| FACP | 提供 SMI 命令端口、PM1a/PM1b 寄存器基址 | acpi_tb_parse_fadt() |
| MADT | 描述处理器拓扑,用于 C-state 协调 | acpi_parse_madt() |