2. 启动流程总览:从BIOS/UEFI到内核启动的完整路径图

现代x86_64服务器从按下电源键到SMP多核系统完全就绪,经历了一条高度分层的启动链。理解这条路径是剖析SMP启动的前提——因为多核唤醒的时机、触发方式、数据传递都依赖于前序阶段的准备。

2.1 启动路径全景:固件 → Bootloader → 内核

整个启动过程可划分为三个宏观阶段,每个阶段承担不同的职责,并逐步将系统控制权从固件移交到操作系统内核。

阶段 执行主体 核心输出 SMP相关性
固件阶段 BIOS / UEFI 硬件拓扑表、ACPI RSDP、APIC初始化 枚举所有CPU核心,为BSP/AP分配APIC ID
Bootloader阶段 GRUB 2 内核镜像加载到内存、setup_header解析 仅BSP运行,AP仍处于复位向量等待状态
内核初始化阶段 Linux内核 start_kernel → smp_init BSP完成全局初始化后,通过IPI唤醒AP
关键认知:在Bootloader阶段,系统中只有一个CPU核心(BSP)在执行代码。其余核心(AP)在硬件复位后处于等待INIT-SIPI-SIPI序列的暂停状态。SMP的真正启动发生在内核初始化后期。
从固件到内核的完整启动路径 阶段1:固件 BIOS / UEFI POST → 硬件枚举 ACPI表 / APIC初始化 阶段2:Bootloader GRUB 2 加载内核镜像到内存 解析setup_header → 跳转 阶段3:内核初始化 start_kernel smp_init → 唤醒AP SMP全核就绪 启动链关键特征: • 控制权单向传递:固件 → Bootloader → 内核,不可逆 • BSP全程主导,AP在阶段1被枚举后保持暂停,直到阶段3被显式唤醒 • ACPI表是固件向内核传递SMP拓扑信息的关键数据结构

2.2 Bootloader(GRUB)加载内核镜像

GRUB 2是Linux生态中最主流的Bootloader。它负责从磁盘分区(或网络)读取内核镜像,将其加载到物理内存的指定位置,并准备启动参数。

2.2.1 加载流程步骤

  1. 阶段1.5 / 阶段2加载:GRUB从MBR或EFI系统分区加载核心映像,识别文件系统(ext4/xfs/btrfs)。
  2. 读取配置文件:解析 /boot/grub/grub.cfg,显示菜单或使用默认条目。
  3. 加载内核与initrd:将vmlinuz和initramfs镜像读入物理内存。内核镜像被放置在 0x100000(1MB)以上的区域。
  4. 设置启动参数:填充 struct boot_params,包括命令行、内存布局、APIC信息等。
  5. 跳转到内核入口:GRUB执行 ljmp 到内核的 startup_32(32位入口)或 startup_64(64位入口)。
内存布局要点:内核镜像头部包含 setup_header(位于偏移0x1F1处),GRUB通过该结构体获取内核版本、加载标志、堆栈指针等关键参数。如果 setup_header 中的 loadflags 字段设置了 LOADED_HIGH 位,内核将被加载到高端内存(>16MB)。
GRUB加载内核后的物理内存布局 0x0000 - 0x100000 实模式中断向量表 BIOS数据区 GRUB运行时栈 0x100000 - 内核末尾 setup_header (0x1F1偏移) startup_32 / startup_64 解压后的内核主体 高端内存区域 initramfs (initrd) 临时根文件系统 包含驱动和启动脚本

2.3 SMP启动的宏观阶段划分

SMP(对称多处理)启动不是一蹴而就的,它被精心设计为三个宏观阶段,确保在唤醒其他核心之前,BSP已经完成了全局数据结构和中断控制器的初始化。

阶段 名称 执行函数/位置 核心动作
阶段A BSP单核初始化 start_kernel() → setup_arch() 解析ACPI/MADT表,构建cpu_possible_mask,初始化本地APIC
阶段B AP核准备就绪 smp_init() → smp_boot_cpus() BSP向每个AP发送INIT-SIPI-SIPI序列,AP执行trampoline代码进入保护模式
阶段C 全核协同运行 cpu_startup_entry() 所有核心进入idle循环,调度器接管,负载均衡开始工作
常见误区:很多人认为SMP启动是从 smp_init() 开始的。实际上,BSP在 setup_arch() 中就已经通过解析ACPI表确定了系统中存在多少个CPU核心,并预留了资源。如果ACPI表损坏或缺失,SMP启动将直接失败,系统退化为单核模式。
SMP启动三阶段宏观流程 阶段A:BSP单核初始化 start_kernel → setup_arch 解析ACPI/MADT表 构建cpu_possible_mask 初始化本地APIC 阶段B:AP核唤醒 smp_init → smp_boot_cpus 发送INIT-SIPI-SIPI序列 AP执行trampoline代码 AP进入保护/长模式 阶段C:全核协同 cpu_startup_entry 所有核心进入idle 调度器负载均衡 SMP系统就绪 关键时序约束: 阶段A必须在阶段B之前完成,因为AP唤醒需要BSP已经设置好全局页表、IDT和APIC向量。阶段B中AP的唤醒顺序由ACPI表指定,通常按APIC ID升序。

2.4 关键数据结构:启动参数传递

从GRUB到内核,以及BSP到AP之间的数据传递依赖几个关键结构体。理解它们有助于后续章节深入分析代码。

  • boot_params:位于实模式内核头部,GRUB填充。包含 hdr(setup_header)、e820_map(内存布局)、edid_info 等。
  • zeropage:内核在进入保护模式前将 boot_params 复制到 ZERO_PAGE(0x1000),后续初始化全程依赖此结构。
  • acpi_table_header / MADT:固件通过ACPI表向内核传递CPU拓扑。MADT(Multiple APIC Description Table)包含每个CPU核心的Local APIC ID、Flags(是否启用)等信息。
  • smp_ops:内核抽象出的SMP操作方法集,包含 .smp_boot_cpus、.smp_cpus_done 等回调,允许不同架构(x86/arm64)定制SMP启动行为。
调试切入点:在启动早期,可以通过 early_printk 或 debug_early 参数打印 boot_params.hdr 中的 code32_start 字段,验证GRUB是否正确设置了内核入口点。如果该值为0,说明Bootloader阶段加载失败。

2.5 本章小结