3. x86架构SMP硬件基础:APIC体系结构与缓存一致性
SMP(对称多处理)系统的核心在于多个CPU核心能够协同工作、共享内存并对外设中断进行统一管理。x86架构为实现这一目标,设计了APIC(高级可编程中断控制器)体系结构,并依赖缓存一致性协议(MESI)来保证数据同步。本章将深入剖析这些硬件基础,为理解Linux内核的SMP启动流程奠定基石。
3.1 APIC体系结构:LAPIC与I/O APIC
APIC体系结构取代了传统的8259A PIC,分为两个关键组件:本地APIC(LAPIC)和I/O APIC。LAPIC集成在每个CPU核心内部,负责处理该核心的中断;I/O APIC则作为系统芯片组的一部分,负责收集外部设备的中断请求并将其路由到指定的LAPIC。
3.1.1 本地APIC(LAPIC)
每个CPU核心都拥有一个独立的LAPIC单元,其内部包含一组寄存器,用于控制中断的接收、发送和优先级管理。LAPIC的主要功能包括:
- 处理本地中断源: 包括时钟中断(Timer)、性能监控计数器中断、温度传感器中断等。
- 处理外部中断: 接收来自I/O APIC路由过来的外部设备中断。
- 处理核间中断(IPI): 接收和发送IPI,用于CPU之间的通信和同步。
- 中断优先级管理: 通过任务优先级寄存器(TPR)和处理器优先级寄存器(PPR)实现中断嵌套和屏蔽。
LAPIC通过MMIO(内存映射I/O)方式访问,其寄存器基地址通常映射在物理地址 0xFEE00000 处。Linux内核通过 lapic 映射基地址来操作这些寄存器。
cat /proc/cpuinfo | grep "apicid" 查看每个CPU核心的LAPIC ID。APIC ID是系统唯一标识每个CPU核心的硬件ID。
3.1.2 I/O APIC
I/O APIC是系统芯片组(如Intel的ICH系列或PCH系列)的一部分,负责收集所有外部设备的中断请求。它拥有24个或更多的中断输入引脚(INTIN0~INTIN23),每个引脚可以独立编程,指定中断的目标CPU、触发方式和中断向量。
I/O APIC的核心数据结构是中断重定向表(Redirection Table),该表包含多个条目,每个条目对应一个中断输入引脚。条目中的关键字段包括:
| 字段 | 描述 |
|---|---|
| Vector | 中断向量号(0-255),决定中断服务例程(ISR)的入口地址。 |
| Delivery Mode | 中断投递模式(Fixed、Lowest Priority、SMI、NMI等)。 |
| Destination Mode | 目标寻址模式(Physical或Logical)。 |
| Destination | 目标LAPIC ID或逻辑目标掩码。 |
| Trigger Mode | 触发方式(Edge或Level)。 |
| Mask | 中断屏蔽位,置1时该中断被暂时禁用。 |
通过编程重定向表,操作系统可以实现灵活的中断负载均衡,将不同设备的中断分发到不同的CPU核心上处理。
3.1.3 LAPIC与I/O APIC协作流程
下图展示了外部设备中断从产生到被CPU处理的完整路径:
3.2 CPU核间中断(IPI)
IPI是SMP系统中CPU之间进行通信和同步的核心机制。一个CPU可以通过向目标CPU的LAPIC发送IPI消息,触发目标CPU执行特定的中断服务例程。IPI在Linux内核启动、调度、TLB刷新等场景中扮演着至关重要的角色。
3.2.1 IPI的发送与接收
发送IPI的过程涉及对本地LAPIC的中断命令寄存器(ICR)进行编程。ICR是一个64位寄存器,分为低32位和高32位:
- ICR低32位(0xFEE00300): 包含中断向量号、投递模式、目标模式等控制字段。
- ICR高32位(0xFEE00310): 包含目标LAPIC ID(物理模式)或逻辑目标掩码(逻辑模式)。
发送IPI的典型步骤如下:
- 读取ICR寄存器,确保没有正在进行的IPI发送(
Delivery Status位为0)。 - 设置ICR高32位,写入目标LAPIC ID或逻辑掩码。
- 设置ICR低32位,写入中断向量号、投递模式等,并触发发送。
- 等待ICR的
Delivery Status位变为0,表示IPI发送完成。
3.2.2 IPI的常见类型
| IPI类型 | 投递模式 | 用途 |
|---|---|---|
| INIT IPI | INIT | 将目标CPU复位到初始化状态,用于SMP启动时的AP唤醒。 |
| STARTUP IPI | STARTUP | 引导目标CPU从指定物理地址(通常是0x0000FFF0)开始执行代码。 |
| Fixed IPI | Fixed | 发送一个固定中断向量号,用于通用核间通信(如函数调用、TLB刷新)。 |
| NMI IPI | NMI | 发送不可屏蔽中断,用于紧急情况(如看门狗超时、调试)。 |
| Self IPI | Fixed | CPU向自身发送中断,用于触发软中断或任务调度。 |
3.2.3 IPI在SMP启动中的应用
在SMP启动过程中,BSP使用IPI序列来唤醒所有AP。典型的唤醒序列如下:
- 发送INIT IPI: BSP向所有AP(除自身外)发送INIT IPI,使AP进入复位状态。
- 等待10毫秒: 确保所有AP完成复位。
- 发送STARTUP IPI: BSP向所有AP发送第一个STARTUP IPI,指定启动代码地址(通常为0x0000FFF0)。
- 等待200微秒: 给AP足够的时间开始执行启动代码。
- 发送第二个STARTUP IPI: 如果AP未成功启动,BSP会再次发送STARTUP IPI。
- AP确认: AP启动后,会通过写入特定的内存位置(如
ap_wakeup_flag)来确认自己的状态。
arch/x86/kernel/smpboot.c 的 do_boot_cpu() 函数中。该函数通过调用 apic->send_IPI() 系列函数来发送IPI。
3.3 系统总线与缓存一致性协议(MESI)
在SMP系统中,每个CPU核心都拥有自己的私有缓存(L1、L2),而所有核心共享主内存。当多个核心同时访问同一内存地址时,必须保证缓存数据的一致性,否则会导致数据错误。系统总线负责连接所有CPU核心、内存控制器和I/O设备,而MESI协议则是维护缓存一致性的核心机制。
3.3.1 系统总线的作用
系统总线(如Intel的QPI或Ultra Path Interconnect)是SMP系统的通信主干道,其主要功能包括:
- 数据传输: 在CPU、内存和I/O设备之间传输数据。
- 地址传输: 传输内存访问地址。
- 控制信号: 传输读写请求、中断消息、缓存一致性协议消息等。
每个CPU核心通过系统总线监听(Snoop)其他核心的总线事务,这是MESI协议实现的基础。
3.3.2 MESI协议详解
MESI协议为每个缓存行(Cache Line,通常为64字节)定义了四种状态,用于描述该缓存行在多个CPU缓存中的一致性状态:
| 状态 | 全称 | 描述 |
|---|---|---|
| M | Modified(已修改) | 缓存行仅存在于当前CPU的缓存中,且已被修改(与主内存不一致)。该CPU拥有该缓存行的唯一所有权。 |
| E | Exclusive(独占) | 缓存行仅存在于当前CPU的缓存中,且与主内存一致。该CPU拥有独占访问权。 |
| S | Shared(共享) | 缓存行存在于多个CPU的缓存中,且所有副本都与主内存一致。 |
| I | Invalid(无效) | 缓存行在当前CPU的缓存中无效,需要从主内存或其他CPU的缓存中重新获取。 |
3.3.3 MESI协议状态转换
MESI协议的状态转换由CPU的本地读写请求和总线上的监听事务驱动。下图展示了核心的状态转换关系:
3.3.4 MESI协议与SMP启动的关系
在SMP启动过程中,MESI协议确保了BSP和AP之间共享数据的一致性。例如:
- 启动代码共享: BSP将AP的启动代码(如
trampoline代码)复制到低内存区域(如0x0000FFF0)。MESI协议确保所有AP在读取该代码时,能够获取到BSP写入的最新版本。 - 全局变量同步: 内核使用全局变量(如
cpu_online_mask)来跟踪AP的状态。MESI协议保证了BSP对变量的修改能够被AP及时看到。 - 锁机制: 在AP唤醒过程中,BSP和AP之间需要通过自旋锁等同步机制来协调。MESI协议是这些锁机制正确运行的硬件基础。
3.4 本章小结
本章深入剖析了x86架构SMP系统的三大硬件基础:
- APIC体系结构: LAPIC负责每个CPU的中断处理,I/O APIC负责外部中断的路由,两者通过系统总线协作。
- CPU核间中断(IPI): IPI是CPU之间通信的核心机制,在SMP启动中用于唤醒AP。
- 缓存一致性协议(MESI): MESI协议通过四种状态(M/E/S/I)和总线嗅探机制,保证了多核环境下缓存数据的一致性。