2. SMP系统基础:对称多处理架构、CPU拓扑结构、ACPI与设备树描述
对称多处理(SMP)是现代多核处理器系统的核心架构,也是Linux内核启动流程中CPU热插拔与SMP协同的基础。本章深入剖析SMP系统的三大支柱:对称多处理架构、CPU拓扑结构,以及硬件描述标准(ACPI与设备树)。
2.1 对称多处理架构(SMP)
SMP架构的核心特征是所有处理器核心(CPU Core)共享同一物理内存地址空间,并通过共享总线或交叉开关互联。每个核心运行独立的指令流,但访问同一份操作系统内核与应用程序代码。
- 对称性:所有CPU核心在功能上对等,无主从之分(BSP在启动阶段临时特殊,启动后变为普通核心)。
- 共享内存:所有核心通过统一地址空间访问内存,缓存一致性协议(如MESI)保证数据同步。
- 并发执行:多个核心可同时执行不同进程或线程,提升系统吞吐量。
- 一致性:硬件保证内存访问顺序与原子操作,操作系统无需额外同步即可实现基本互斥。
下图展示了SMP系统的典型架构,其中所有CPU核心通过系统总线连接到共享内存与I/O控制器:
2.2 CPU拓扑结构
CPU拓扑描述了处理器核心之间的层级关系,包括:核心(Core)、线程(Thread/HT)、簇(Cluster)、NUMA节点(Node)等。Linux内核通过cpu_topology结构体管理这些层级,用于调度优化、负载均衡和功耗管理。
| 拓扑层级 | Linux表示 | 典型粒度 | 调度影响 |
|---|---|---|---|
| 线程(SMT) | thread_sibling |
超线程兄弟 | 共享L1/L2缓存,适合细粒度并行 |
| 核心(Core) | core_sibling |
物理核心 | 共享L2缓存,任务亲和性绑定 |
| 簇(Cluster) | cluster_sibling |
多核组 | 共享L3缓存,跨簇通信延迟较高 |
| NUMA节点 | numa_node |
物理CPU插槽 | 内存访问延迟差异,远端内存访问慢 |
下图展示了一个典型的ARM big.LITTLE CPU拓扑结构,包含两个簇(性能簇与效率簇),每个簇包含4个核心:
2.3 ACPI与设备树描述
ACPI(高级配置与电源接口)和设备树(Device Tree)是两种主流的硬件描述标准,用于向操作系统传递CPU拓扑、中断控制器、内存映射等静态信息。Linux内核在启动初期解析这些信息,构建CPU拓扑结构并初始化SMP子系统。
2.3.1 ACPI(x86/ARM64服务器)
ACPI使用表格(Table)描述硬件,关键表格包括:
- MADT(Multiple APIC Description Table):描述CPU核心、GIC(通用中断控制器)、IO-APIC等中断控制器信息。
- SRAT(System Resource Affinity Table):描述CPU与内存的NUMA亲和性。
- SLIT(System Locality Information Table):描述NUMA节点间的访问延迟。
- DSDT(Differentiated System Description Table):包含AML字节码,描述设备树状结构。
Linux内核通过acpi_parse_madt()函数解析MADT表,提取每个CPU核心的APIC ID,并构建cpu_possible_mask位图:
// 内核源码示例:arch/x86/kernel/acpi/boot.c
static int __init acpi_parse_madt_lapic_entries(void)
{
int count;
// 解析MADT表中的Local APIC条目
count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC,
acpi_parse_lapic, MAX_LOCAL_APIC);
if (count <= 0) {
pr_err("No LAPIC entries found\n");
return -ENODEV;
}
// 标记所有解析到的CPU为possible
for_each_present_cpu(cpu)
set_cpu_possible(cpu, true);
return count;
}
2.3.2 设备树(ARM32/ARM64嵌入式)
设备树使用.dts(Device Tree Source)文件描述硬件,编译为.dtb(Device Tree Blob)传递给内核。关键节点包括:
- cpus节点:包含每个CPU核心的子节点,定义
reg(MPIDR)、enable-method(启动方式,如psci、spin-table)。 - psci节点:描述电源状态协调接口,用于CPU热插拔和挂起。
- interrupt-controller节点:描述GIC(通用中断控制器)的基地址和中断路由。
典型的设备树CPU描述片段:
// 设备树片段:cpus节点
cpus {
#address-cells = <2>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a76";
reg = <0x0 0x0>; // MPIDR[1:0] = 0
enable-method = "psci"; // 使用PSCI唤醒
next-level-cache = <&l3_cache_0>;
};
cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a76";
reg = <0x0 0x100>; // MPIDR[1:0] = 1
enable-method = "psci";
next-level-cache = <&l3_cache_0>;
};
cpu@100 {
device_type = "cpu";
compatible = "arm,cortex-a55";
reg = <0x0 0x10000>; // MPIDR[1:0] = 0, 簇=1
enable-method = "psci";
next-level-cache = <&l3_cache_1>;
};
};
下图对比了ACPI与设备树在SMP启动中的角色与流程:
- ACPI 是动态描述,AML字节码可在运行时执行,支持热插拔和电源管理事件;适用于x86和ARM64服务器。
- 设备树 是静态描述,编译后不可变,解析速度快;适用于嵌入式系统,启动时一次性加载。
- 热插拔支持:ACPI通过
_EJ0和_STA方法支持CPU热移除/热添加;设备树依赖PSCI或厂商自定义机制。
2.4 从硬件描述到内核拓扑
Linux内核在启动初期(start_kernel → setup_arch)解析ACPI或设备树,构建以下关键数据结构:
| 数据结构 | 含义 | 初始化时机 |
|---|---|---|
cpu_possible_mask |
系统中可能存在的所有CPU | 解析MADT/cpus节点后 |
cpu_present_mask |
当前已上电的CPU | 热插拔状态更新时 |
cpu_online_mask |
已启动并参与调度的CPU | 每个CPU完成初始化后 |
cpu_active_mask |
可运行任务的CPU(热插拔迁移后) | 热插拔回调完成后 |
cpu_topology |
每个CPU的拓扑层级(thread/core/cluster/node) | 解析拓扑信息后 |