BIOS 与 UEFI
BIOS (Basic Input/Output System) 和 UEFI (Unified Extensible Firmware Interface) 是计算机系统启动时最先运行的固件程序,负责初始化硬件并将控制权交给操作系统。理解它们的工作原理对于系统开发和故障排查具有重要意义。
BIOS
BIOS 是传统 PC 的固件接口,起源于 1975 年的 CP/M 操作系统,在 IBM PC 时代成为行业标准。BIOS 固化在主板上的 ROM 或 EEPROM 芯片中,计算机通电后首先执行 BIOS 代码。
BIOS 的启动过程分为若干阶段。首先执行上电自检 (POST),检测和初始化 CPU、内存、显卡等关键硬件。然后枚举总线设备,为 PCI 设备分配中断和 I/O 资源。接着按照启动顺序扫描存储设备,找到活动分区的引导扇区并执行引导代码。最后将控制权交给引导加载程序,完成操作系统的启动。
BIOS 的工作模式基于 16 位实模式,只能访问 1MB 以下的内存空间。这意味着即使计算机有数 GB 的内存,BIOS 也只能使用最底部的 640KB 基本内存和 384KB 上位内存。BIOS 的中断调用(如 INT 10h 显示服务、INT 13h 磁盘服务)是传统的软硬件接口方式,虽然简单但扩展性差。
从工程角度看,BIOS 的局限性日益明显。它无法初始化大于 2.2TB 的硬盘,因为传统的 MBR 分区表只支持 32 位扇区数。BIOS 的图形能力极其有限,现代操作系统的启动画面需要在保护模式自行初始化显卡。BIOS 的配置界面基于文本菜单,用户体验较差,而且硬件配置信息存储在非易失性 CMOS 内存中,容易因电池失效而丢失。
UEFI
UEFI 是 BIOS 的继任者,由 Intel 发起并由业界多家公司共同制定的下一代固件接口标准。UEFI 最初名为 EFI,在 Itanium 架构上首次使用,后来扩展到 x86-64 平台并成为主流。
UEFI 从设计之初就面向 32 位或 64 位保护模式,可以访问全部物理内存。这意味着 UEFI 可以直接加载大型 EFI 可执行文件,无需像 BIOS 那样先执行 512 字节的引导扇区代码。UEFI 使用 GUID 分区表 (GPT),支持最大 2^64 个扇区,远超 MBR 的 2^32 限制,能够初始化数 TB 甚至 EB 级别的硬盘。
UEFI 的架构分为几个层次。最底层是平台初始化,负责硬件的早期初始化和一致性协议。中间层是驱动执行环境,提供设备驱动和服务的加载框架。最上层是操作系统引导,负责加载引导程序并传递硬件信息。
UEFI 的一个显著特性是支持独立的 EFI 系统分区 (ESP)。ESP 是一个小的 FAT 分区,存放引导加载程序和驱动程序。ESP 的存在使得操作系统可以独立于平台固件进行维护,升级系统时无需刷新固件。UEFI 还内置了网络协议栈,支持网络启动 (PXE) 和远程诊断。
从用户体验来看,UEFI 提供图形化配置界面,支持鼠标操作,可以显示详细的硬件信息。UEFI 固件通常存储在闪存中,可以通过固件刷新工具进行升级。UEFI 还定义了安全启动 (Secure Boot) 机制,通过验证引导加载程序和内核的数字签名,防止恶意软件在启动阶段注入。
BIOS 与 UEFI 的启动流程差异
BIOS 启动流程采用接力方式,BIOS 完成硬件初始化后读取 MBR 的引导代码,引导代码再加载活动分区的引导加载程序。MBR 只有 512 字节,空间极其有限,因此引导加载程序通常分为多个阶段,第一阶段的引导代码负责加载第二阶段的完整代码。这种多阶段引导方式增加了复杂性,而且 MBR 只能支持四个主分区或三个主分区加一个扩展分区。
UEFI 启动流程更加直接,UEFI 固件可以直接读取 ESP 分区中的 EFI 可执行文件。ESP 是标准的 FAT 文件系统,可以存放多个引导加载程序,用户可以在启动时选择不同的操作系统或内核版本。UEFI 不需要链式引导,减少了中间环节,提高了启动可靠性。
从 Linux 内核的角度来看,BIOS 和 UEFI 提供的系统信息格式也不同。BIOS 通过中断调用获取硬件信息,而 UEFI 提供系统表 (System Table) 和配置表 (Configuration Tables),内核在启动早期可以解析这些表格获取内存映射、ACPI 表、图形设备等关键信息。
ACPI 硬件抽象层
ACPI (Advanced Configuration and Power Interface) 是由 Intel、Microsoft、HP 等公司共同制定的硬件配置和电源管理标准。ACPI 提供了一种抽象机制,使操作系统无需了解硬件的具体实现细节,就能完成设备配置和电源管理。
ACPI 的核心思想是将硬件配置信息以数据表的形式提供给操作系统。这些表由固件 (BIOS 或 UEFI) 在启动时构建,操作系统通过解析这些表来获取硬件拓扑、设备资源、电源管理方法等信息。ACPI 表包括 RSDP (Root System Description Pointer)、RSDT (Root System Description Table)、FADT (Fixed ACPI Description Table)、DSDT (Differentiated System Description Table)、SSDT (Secondary System Description Table) 等多个表格。
DSDT 是最重要的 ACPI 表,它包含主板的设备定义和控制方法。这些方法用 ACPI Machine Language (AML) 编写,是一种解释执行的字节码语言。当操作系统需要执行某个硬件操作时,会调用相应的 ACPI 方法,ACPI 解释器执行 AML 代码,完成对硬件寄存器的操作。
从工程角度看,ACPI 实现了硬件抽象层的理念。操作系统不需要知道"如何操作某个寄存器来控制 PCIe 设备的复位信号",只需要调用 ACPI 定义的 _RST 方法即可。这种方法大大降低了操作系统的硬件依赖性,使得同一个 Windows 或 Linux 系统可以在成千上万种不同的主板上运行。
但 ACPI 也有其局限性。AML 是一种晦涩的字节码语言,难以阅读和调试。ACPI 表通常由 BIOS 厂商闭源提供,开发者无法修改或查看具体的硬件操作逻辑。当出现硬件兼容性问题时,开发者往往束手无策,只能等待 BIOS 厂商发布更新。
与设备树相比,ACPI 更加动态和灵活。设备树是静态的硬件描述,编译进内核后无法修改。而 ACPI 方法可以包含条件判断和动态逻辑,可以根据运行时的状态做出不同决策。这种灵活性在 PC 场景是必要的,因为 PC 的硬件配置变化多样,设备树难以覆盖所有组合。
ACPI 的另一个重要功能是电源管理。ACPI 定义了系统电源状态 (G0-S5)、设备电源状态 (D0-D3)、处理器电源状态 (C0-C3) 等标准。操作系统通过调用 ACPI 方法,可以让系统进入睡眠、休眠状态,可以让设备进入低功耗模式,可以控制处理器的频率和电压。这些电源管理功能对于笔记本电脑和移动设备尤为重要,能够显著延长电池续航时间。
Linux 对 BIOS 和 UEFI 的支持
Linux 内核同时支持 BIOS 和 UEFI 启动方式。对于 BIOS 系统,内核使用传统的实模式切换,在保护模式初始化后需要手动探测硬件设备。对于 UEFI 系统,内核可以利用 UEFI 提供的内存映射和设备信息,减少硬件探测的盲目性。
在实际工程应用中,UEFI 系统的优势更加明显。UEFI 启动速度通常更快,因为避免了实模式切换和多次引导阶段。UEFI 的图形化启动界面可以显示启动进度和故障信息,便于诊断问题。UEFI 还支持从大于 2TB 的磁盘启动,这对存储密集型应用非常重要。
但 BIOS 系统仍有其存在价值。一些老旧的服务器和嵌入式设备仍在使用 BIOS,而且 BIOS 的简单性使得它更适合资源受限的嵌入式场景。从开发角度来看,理解 BIOS 的工作原理有助于理解计算机系统的底层机制,这对于系统级编程和故障排查都是有益的。