64 位多处理器初始化
本文主要讲解 64 位多处理器相关的运行架构,及其初始化。
多处理器概念
最初的多处理器是在一个计算机系统内同时存在多个 CPU 的封装,简单理解就是在一个主板上插多个 CPU。这种系统叫做「对称多处理器系统」,简称 SMP 系统。

而现如今更多的是在同一个封装内封装多个核心。换句话说,多处理器系统位于同一块儿芯片上。
- BSP:bootstrap processor,启动处理器,启动时由仲裁电路选出。最开始只有 BSP 在工作,其他核心处于等待状态。
- AP:application processor,应用处理器,由 BSP 分配任务并执行。
同时多线程(SMT)
气泡
我们知道 IA-32 架构引入了流水线技术,指令可以拆解成微操作,并放入流水线中同时执行。但如果流水线中存在气泡,则硬件利用率就会变低。
气泡,也叫流水线停顿(Pipeline Stall),就是指在流水线的某个阶段,没有进行任何有效工作的时钟周期。比如一个微操作未命中高速缓存,必须访问内存,而访问内存是耗时操作,则微操作处于停顿状态,这个强制性的空闲、等待或阻塞的周期,就是“气泡”。它像一个没有实际指令数据的“气泡”一样,在流水线中向后移动。
气泡产生的原因
为什么会产生气泡?气泡的产生是因为出现了冒险(Hazard),即指令之间的依赖关系导致它们不能在流水线中顺畅地重叠执行。
- 数据冒险(Data Hazard):当一条指令需要依赖另一条指令的计算结果,但这个结果还没产生时,就会发生数据冒险。
- 控制冒险(Control Hazard):当 CPU 遇到分支指令(如 if-else、循环)时,它需要根据条件判断下一条指令是谁(是跳转,还是顺序执行?)。在判断出结果之前,流水线无法确定下一条要取哪条指令。
- 结构冒险(Structural Hazard):当多条指令试图在同一时钟周期使用同一个硬件资源时发生。
处理器可以同时启动并执行多条不相干的代码流(线程)。通过混杂多个线程的指令,可以减少气泡,提高整体运行效率。这种混杂执行多个线程的指令的运行方式叫做同时多线程(Simultaneous Multi-Threading, SMT)。
INTEL 超线程技术
同时多线程是这种技术的名称,但实际实现上,各家有各家的叫法。在 INTEL 这里,就是我们常听说的「INTEL 超线程技术」。

高级中断控制器(APIC)
APIC 核心作用是取代传统的中断控制器(如 8259A PIC),以支持多处理器环境、提供更多的中断资源,并实现更灵活和高效的中断处理。它主要有以下特点:
- 支持多处理器系统:APIC 是 Intel 多处理规范的核心,它允许中断请求被精确地路由到指定的处理器核心,实现了多个处理器之间的中断分配与负载均衡。系统中的每个 CPU 都有一个本地 APIC(Local APIC),同时还有一个或多个 I/O APIC 来处理来自外部设备的中断。
- 扩展中断数量:传统的 8259A PIC 仅支持 8 个中断请求(IRQ),这在现代计算机中远远不够。APIC 系统支持多达 255 个中断向量(虽然 0-15 通常被保留),为大量硬件设备提供了充足的中断资源。
- 处理处理器间中断(IPI):APIC 使得一个处理器能够通过中断的方式向另一个处理器发送消息,这对于多处理器操作系统进行任务调度、缓存同步等多核协作操作至关重要

现代 x86 处理器中有三个中断引脚:
- LINT 0:用于接收来自 8259 或类 8259 芯片的中断 INTR。
- LINT 1:通常用于来接收不可屏蔽中断 NMI
- 中断消息引脚连接到 APIC 总线,用于接收 I/O APIC 的消息,和 IPI 消息(处理器间中断消息)
APIC 之 间需要相互识别,所以需要有一个 APIC ID。APIC ID 不是固定不变的,而是动态分配,在加电后根据系统拓扑自动生成。即,根据每个 APIC 所在的位置生成,比如位于哪个封装的哪个核心内。
在多处理器系统中,APIC ID 就是它所在的处理器 ID。APIC 中有一个寄存器专门用来存储 APIC ID 的,APIC ID 的长度决定了这个系统中可以有多少个处理器。比如 APIC ID 长度是 8 位,则最多可以有 255 个处理器。
早期要组成多处理器系统,必须使用外置的 APIC 芯片,如 82489DX 芯片,它使用 8 位的 APIC ID,最多支持 255 个处理器。
后来从奔腾系列开始,APIC 被内置在 CPU 中,叫做 Local APIC。最初应该是对规模预估不足,只留了 4 位给 APIC ID。一个系统最多支持 15 个处理器。后来有对 Local APIC 做了扩展,变成 16 位的 xAPIC。xAPIC 一直延续到现在,最近又出现了 x2APIC 模式,使用 32 位的 APIC ID,最多支持个处理器。
地址映射:Local APIC 内部的寄存器被直接映射到内存区域,地址是 0xFEE0 0000 开始的 4KB 区域。部分处理器支持通过IA32_APIC_BASE型号专属寄存器修改映射的起始位置。I/O APIC 也直接映射到内存,默认地址是 0xFEE0 0000。
多处理器系统的启动过程
- 加电复位,基于总线拓扑,生成 APIC ID。
- 各处理器执行自检
- 通过 APIC 仲裁出自举处理器 BSP。BSP 的
IA32_APIC_BASE寄存器置 1。 - BSP 执行 BIOS 自举代码。所有 AP 都是「等待 SIPI」的状态。
- BSP 执行 BIOS 初始化代码,BIOS 在内存创建 ACPI 或 MP 表,并将 BSP 的 APIC ID 填入表中。
- BSP 广播 SIPI 给 AP,要求他们从指定物理地址处开始执行初始化代码。
- AP 初始化。用全局变量统计 AP 数量,并将自己的 APIC ID 写入 ACPI 或 MP 表。
- 一旦 AP 都完成了上述初始化过程,BSP 继续执行剩余 BIOS 代码,执行操作系统自举和初始化。
- 操作系统启动后,基于负载均衡策略,周期性使用中断消息给 AP 分配广义上的线程。
在计算机启动过程中,为了让操作系统能够准确识别和初始化系统中的所有处理器核心,并建立起正确的多处理器中断通信机制。操作系统需要获取 APIC ID 信息,具体来说是为了识别多处理器拓扑结构。这是操作系统进行任务调度、资源分配的基础。
APIC ID 信息可以从 ACPI(高级配置和电源接口)表中获取,所以我们需要研究一下 ACPI。
高级配置和电源接口(ACPI)
ACPI(高级配置与电源接口)协议是 90 年代中期由英特尔、微软、东芝、凤凰等公司联合创立的,主要是为了解决计算机硬件配置、电源管理以及操作系统与硬件固件(BIOS/UEFI)之间通信的标准化问题。
ACPI 协议的核心价值体现在以下几个方面:
- 统一的电源管理标准:在 ACPI 出现之前,不同厂商的硬件和操作系统在电源管理(如休眠、待机)的实现上各不相同,导致兼容性问题和用户体验不一致。ACPI 提供了一套统一的规范,使得操作系统能够以一种标准的方式管理不同硬件平台的电源状态。
- 增强的硬件配置和热插拔支持:ACPI 允许操作系统动态地识别和配置硬件设备,这对于支持热插拔(Hot-Plug) 功能(如 USB、PCIe 设备)至关重要。它提供了一个抽象的接口来描述硬件资源,减轻了操作系统直接与复杂硬件细节打交道的负担。
- 操作系统控制的电源管理(OSPM)模型:ACPI 确立了一种由操作系统作为控制中心来管理整个系统电源的策略,而不是由 BIOS 单独负责。这使得电源管理更加智能和高效,操作系统可以根据实际运行的应用和负载情况,动态地调整处理器、芯片组和设备的功耗状态。
- 提供硬件信息接口:ACPI 通过在内存中构建一系列表格(如 RSDP, RSDT, XSDT, FADT, DSDT, SSDT 等),向操作系统提供丰富的硬件配置信息,包括主板设备、电池、温度传感器等。这些信息对于操作系统的设备驱动程序和电源管理策略至关重要。
ACPI 的数据结构和表
各专有名词释义:
| 表格名称 (英文全称) | 签名 (Signature) | 主要功能与描述 | 是否必需 |
|---|---|---|---|
| RSDP (Root System Description Pointer) | RSD PTR (含空格) | 所有 ACPI 表的起始点(入口指针),存在于内存固定区域,操作系统通过它来定位 RSDT 或 XSDT。 | 是 |
| RSDT (Root System Description Table) | RSDT | ACPI 1.0 的根表,包含一个32 位物理地址数组,指向其他 ACPI 表。为兼容性保留。 | 是(ACPI 1.0) |
| XSDT (Extended System Description Table) | XSDT | ACPI 2.0+ 的根表,包含一个64 位物理地址数组,指向其他 ACPI 表。现代系统优先使用此表。 | 是(ACPI 2.0+) |
| MADT (Multiple APIC Description Table) | APIC | 描述系统中断控制器布局(如 IO-APIC、Local APIC)和处理器拓扑结构,支持多处理器(SMP)配置。 | 是 |
| FADT (Fixed ACPI Description Table) | FACP | 包含固定硬件的 ACPI 寄存器块地址(如电源管理寄存器)、睡眠状态控制信息,并指向 DSDT 和 FACS。 | 是 |
| MCFG (PCI Express Memory Mapped Configuration Space Table) | MCFG | 提供 PCI Express 设备内存映射配置空间的基地址信息,用于访问 PCIe 配置空间。 | 是(对于 PCI Express 系统) |
| FACS (Firmware ACPI Control Structure) | FACS | 包含上次启动的硬件签名、固件唤醒向量和全局锁,主要用于 S3 睡眠状态的保存和恢复。 | 是 |
| DSDT (Differentiated System Description Table) | DSDT | 包含系统主要的差异化定义块,定义了系统的实现和配置信息,如设备电源管理、热管理等。其内容由 AML 字节码编写。 | 是 |
| SSDT (Secondary System Description Table) | SSDT | DSDT 的补充,可提供额外的定义块(例如描述其他设备)。系统可以有多个 SSDT。 | 否 |
E820
ACPI 申领到的内存所在地址不是不定不变的,而是在一定的地址范围内分配。为了能够确切地知道 RSDP 所在的位置,需要通过 0x15 号 BIOS 中断发起功能调用。这是一个总入口,需要使用 EAX 指定具体功能号,即指定为 E820。
内存映射数据繁多且复杂,因此需要多次调用 0x15 软中断,分页返回。每次返回的都是一个固定的数据结构,叫做地址范围描述符(Address Range Descriptor)。
地址范围描述符(Address Range Descriptor)
这个结构体有 两个主要版本:
- 基本格式(20 字节) - 最常见,兼容性最好
- 扩展格式(24 字节) - 包含 ACPI 3.0 扩展属性
我们主要讲基本格式。
| 偏移量 (字节) | 长度 (字节) | 名称 | 数据类型 | 描述 |
|---|---|---|---|---|
| 0 | 8 | Base Address | QWORD | 基地址 (64 位):该内存区域的起始物理地址。 |
| 8 | 8 | Length | QWORD | 长度 (64 位):该内存区域的大小(以字节为单位)。 |
| 16 | 4 | Type | DWORD | 类型 (32 位):标识该内存区域的类型(见下方类型表)。这是最关键字段,决定 OS 是否/如何使用该区域。 |
内存区域类型 (Type):
| 值 (十进制) | 值 (十六进制) | 名称 | 描述 |
|---|---|---|---|
| 1 | 0x00000001 | RAM (可用内存) | 操作系统可以正常使用的可用内存。这是操作系统内核和应用程序主要使用的内存。 |
| 2** | 0x00000002 | Reserved (保留内存) | 不可用内存。可能被硬件保留(如 ROM, MMIO 空间)、包含 BIOS 代码或数据、存在硬件问题、或被 ACPI 表标记为保留。操作系统不能使用。 |
| 3 | 0x00000003 | ACPI Reclaimable | ACPI 可回收内存。包含 ACPI 表(如 DSDT, SSDT)。操作系统在初始化阶段可以读取这些表,之后可以回收这部分内存作为普通 RAM 使用。 |
| 4 | 0x00000004 | ACPI NVS Memory | ACPI 非易失性存储内存。被 ACPI 保留用于保存系统状态(如休眠到磁盘 S4)等目的。操作系统不应使用此内存(除非用于 ACPI 指定目的)。 |
| 5 | 0x00000005 | Bad Memory (坏内存) | 包含内存错误,不可用。 |
| 其他 | 其他值可能由特定 BIOS 或硬件定义,应视为保留类型。 |
- EAX:0xe820
- EBX:持续标记,首次使用清零,后续由 BIOS 设置。为 0 证明是最后一页数据,为 1 则应该继续请求。
- ES:DI:指定 ARD 的逻辑段地址和段内偏移量。BIOS 用这个结构返回地址映射数据
- ECX:指定 ARD 的长度,单位:字节。最小为 20 字节。
- EDX:签名,固定位字符串“PAMS”(即物理地址映射结构,Physical Address Mapping Structure)
准备以上参数后,可以发起 0x15 中断。如果执行中没有出现错误,则 CF 被清零。否则 CF=1,同时 EAX 返回“PAMS”,EBX 返回持续标记。
