Linux内核启动流程BIOS系统初始化操作系统引导 - 第2节

2.1 BIOS 的定义与核心功能

BIOS(Basic Input/Output System,基本输入输出系统)是计算机启动时最先运行的固件程序,它负责在操作系统加载之前完成硬件初始化、自检以及提供底层 I/O 服务。BIOS 是硬件与操作系统之间的桥梁,其核心功能可归纳为以下四个方面:

功能模块 说明 典型操作
POST(加电自检) 检测 CPU、内存、芯片组等关键硬件是否正常 读取 CPU 微码、校验内存、检查显卡
硬件初始化 为所有板载设备分配资源并设置默认状态 配置中断控制器、DMA 通道、定时器
中断服务例程 提供 INT 10h(视频)、INT 13h(磁盘)等底层调用 实模式下通过软中断访问硬件
引导加载程序 从指定存储设备读取主引导记录并跳转执行 读取 MBR 到 0x7C00 并跳转
关键概念:BIOS 运行在 x86 实模式下,地址空间为 20 位(1MB),所有操作均通过 I/O 端口和内存映射方式完成。Linux 内核在启动初期会脱离 BIOS 依赖,进入保护模式。

2.2 BIOS 在 ROM 中的存储位置

BIOS 固件存储在主板上的非易失性存储器中,传统上使用 ROM(只读存储器),现代主板多采用 Flash ROM(闪存)以便升级。在 x86 系统的物理地址空间中,BIOS 被映射到特定的高位区域:

  • 传统 BIOS 映射地址:0xFE0000 ~ 0xFFFFFF(1MB 地址空间的顶部 128KB)
  • 现代 BIOS 映射地址:0xFFFE0000 ~ 0xFFFFFFFF(4GB 地址空间的顶部 2MB)
  • 复位向量:CPU 上电后第一条指令从 0xFFFFFFF0(4GB 顶部 16 字节)开始执行,该地址指向 BIOS 入口
x86 物理地址空间中的 BIOS 映射位置 物理地址空间(4GB 范围) BIOS 映射 0x00000000 0xFFFE0000 0xFFFFFFFF 复位向量 0xFFFFFFF0 说明: CPU 上电后从 0xFFFFFFF0 取指,该地址包含一条远跳转指令(JMP), 跳转到 BIOS 实际入口(通常位于 0xFE0000 附近),开始执行 POST 流程。
硬件实现细节:Flash ROM 通过 LPC(Low Pin Count)总线或 SPI(Serial Peripheral Interface)总线与南桥芯片连接。BIOS 芯片容量从早期的 64KB 发展到现在的 16MB 以上,用于存储 UEFI 固件和备份映像。

2.3 CMOS 的作用与存储内容

CMOS(Complementary Metal-Oxide-Semiconductor,互补金属氧化物半导体)是一块低功耗的 RAM 芯片,由主板上的纽扣电池(CR2032)供电,用于保存 BIOS 配置参数和实时时钟信息。CMOS 与 BIOS 的关系是:BIOS 是程序,CMOS 是存储配置数据的存储器。

CMOS 存储的关键数据

偏移地址 存储内容 典型值
0x00 ~ 0x09 实时时钟(RTC):秒、分、时、日、月、年 BCD 编码的时间值
0x10 软盘驱动器类型 0x00(无)/ 0x01(360K)/ 0x02(1.2M)
0x12 硬盘驱动器类型 0x00(无)/ 0x0F(类型 15)
0x14 设备安装标志 位掩码表示已安装设备
0x2E ~ 0x2F 标准 CMOS 校验和 前 0x10 字节的累加和
0x30 ~ 0x3F 扩展配置数据(日期、密码、启动顺序) 厂商自定义
BIOS 与 CMOS 的协作关系 BIOS 固件 存储在 Flash ROM CMOS RAM 电池供电,保存配置 读取/写入配置 纽扣电池 CR2032 供电 CMOS 容量通常为 64 字节(标准)或 128 字节(扩展),通过 I/O 端口 0x70(索引)和 0x71(数据)访问。
常见故障排查:如果 CMOS 电池耗尽,系统会丢失日期、启动顺序等配置,每次开机都会提示“CMOS Checksum Error”。此时需要更换电池并重新设置 BIOS 参数。在 Linux 中,可以通过 hwclock 命令查看和同步 CMOS 时间。

2.4 BIOS 启动流程概览

从 CPU 复位到 BIOS 交出控制权,整个流程可分为以下关键步骤:

  1. CPU 复位:CS 段寄存器设为 0xF000,EIP 设为 0xFFF0,从 0xFFFFFFF0 取指。
  2. 跳转到 BIOS 入口:执行远跳转指令,进入 BIOS 代码区(0xFE0000 附近)。
  3. POST 自检:检测 CPU、内存、芯片组、显卡等关键硬件,通过蜂鸣器或诊断卡报告错误。
  4. 硬件枚举与初始化:为 PCI 设备分配资源,初始化中断控制器和定时器。
  5. 读取 CMOS 配置:加载用户设置的启动顺序、设备参数等。
  6. 中断向量表初始化:在实模式内存低端(0x00000 ~ 0x003FF)建立中断向量表。
  7. 引导加载:按 CMOS 中设置的启动顺序,尝试从磁盘、光驱或网络读取引导扇区。
BIOS 启动流程(从复位到引导) CPU 复位(0xFFFFFFF0) 跳转到 BIOS 入口 POST 自检 硬件枚举与初始化 读取 CMOS 配置 引导加载(读取 MBR)
性能指标:传统 BIOS 的 POST 过程通常耗时 2~5 秒,而 UEFI 固件通过快速启动(Fast Boot)可将此时间缩短至 1 秒以内。Linux 内核在启动日志中会记录 BIOS 提供的硬件信息,可通过 dmesg | grep -i bios 查看。

2.5 BIOS 与 Linux 内核的交互点

在 Linux 内核启动的早期阶段,内核会通过以下方式与 BIOS 交互:

  • 实模式入口:引导加载程序(如 GRUB)将内核从磁盘加载到内存后,内核首先在实模式下执行 arch/x86/boot/header.S 中的代码,利用 BIOS 中断读取硬件参数。
  • E820 内存映射:通过 BIOS 中断 INT 15h 功能号 E820h 获取物理内存布局,这是内核建立页表的基础。
  • 视频模式设置:通过 INT 10h 设置显示模式,为早期启动画面提供支持。
  • SMBIOS/DMI 信息:内核通过访问 BIOS 数据区获取主板、BIOS 版本、序列号等系统管理信息。
// 内核源码中获取 E820 内存映射的典型调用(arch/x86/boot/memory.c)
static int detect_memory_e820(void)
{
    int count = 0;
    struct biosregs ireg, oreg;

    initregs(&ireg);
    ireg.ax  = 0xe820;
    ireg.cx  = sizeof(struct e820entry);
    ireg.edx = SMAP;
    ireg.di  = (size_t)&buf;

    do {
        intcall(0x15, &ireg, &oreg);
        ireg.ebx = oreg.ebx;
        if (oreg.eflags & X86_EFLAGS_CF)
            break;
        count++;
    } while (ireg.ebx && count < ARRAY_SIZE(buf));

    return count;
}
学习建议:理解 BIOS 固件基础是掌握 Linux 内核启动流程的第一步。建议在虚拟机中通过 qemu -bios 选项指定不同 BIOS 映像,观察启动日志变化,加深对 BIOS 映射地址和 CMOS 配置的理解。