3. ARM64架构基础(下)

在调度器移植适配过程中,ARM64 的内存管理单元(MMU)、缓存一致性协议以及原子操作指令是决定内核能否正确、高效运行的关键硬件基础。本章深入剖析这些核心机制,为后续调度器上下文切换、进程地址空间管理及同步原语的实现提供坚实的硬件视角支撑。

3.1 ARM64 内存管理单元(MMU)与地址转换

ARM64 的 MMU 负责将处理器发出的虚拟地址(VA)转换为物理地址(PA),并实施内存属性(如可缓存性、可共享性)控制。调度器在创建进程、切换进程时,必须正确配置 MMU 相关的页表。

3.1.1 虚拟地址空间布局与转换粒度

ARM64 支持 4KB、16KB 和 64KB 三种页粒度,以及 4 级(L0~L3)或 3 级页表结构。以最常见的 4KB 页、48 位虚拟地址空间为例,地址转换过程如下:

  • 输入地址:48 位虚拟地址(VA[47:0])。
  • 页表基址寄存器:TTBR0_EL1(用户空间)和 TTBR1_EL1(内核空间)。
  • 转换级别:L0(9位索引)→ L1(9位)→ L2(9位)→ L3(9位)→ 页内偏移(12位)。
关键点: 调度器在 context_switch 中切换进程时,会通过 switch_mm 更新 TTBR0_EL1 指向新进程的页表基址,并执行 isb 指令保证页表切换生效。
移植提示: ARM64 的 ASID(Address Space ID)机制允许 TLB 中同时缓存多个进程的页表项,避免每次进程切换都刷新 TLB,显著降低调度开销。
ARM64 4级页表地址转换流程(4KB页,48位VA) ARM64 4级页表地址转换流程(4KB页,48位VA) 虚拟地址 VA[47:0] L0索引 [47:39] L1索引 [38:30] L2索引 [29:21] L3索引 [20:12] 页内偏移 [11:0] L0 页表 L1 页表 L2 页表 L3 页表 物理地址 PA[47:0] TTBR0_EL1(用户) / TTBR1_EL1(内核) 指向 L0 页表基址

3.2 页表描述符与内存属性

ARM64 页表描述符不仅包含物理地址,还编码了内存类型、访问权限、可共享性等属性。调度器在管理内核栈、进程描述符时,需要确保这些内存区域被正确映射。

描述符字段 位域 说明 调度器相关场景
输出地址 [47:12] 物理页帧号(PFN) 进程页表映射
AttrIndx [10:8] 指向 MAIR 寄存器中的属性索引 内核栈可缓存性配置
NS [5] 非安全空间标识 虚拟化场景隔离
AP [7:6] 访问权限(EL0/EL1 读写) 用户态/内核态隔离
SH [9:8] 可共享性(非共享/内部共享/外部共享) 多核调度数据一致性
AF [10] 访问标志(Access Flag) 页面回收与缺页统计
nG [11] 非全局位(区分进程特有页) ASID 匹配与 TLB 管理
移植陷阱: 在调度器初始化阶段,如果内核栈所在页面的 MAIR 属性被错误配置为 Device-nGnRE(而非 Normal Cacheable),将导致栈访问性能急剧下降,甚至触发对齐错误。

3.3 缓存架构与一致性协议

ARM64 处理器通常包含多级缓存(L1/L2/L3),并使用 MESI 或其变体(如 MOESI)协议维护缓存一致性。调度器在进程迁移、内核栈切换时必须考虑缓存状态。

3.3.1 缓存层级与行大小

  • L1 缓存:分为指令缓存(I-cache)和数据缓存(D-cache),典型大小为 32KB~64KB,行大小 64 字节。
  • L2 缓存:统一缓存,典型大小 256KB~1MB,行大小 64 字节。
  • L3 缓存:可选,多核共享,典型大小 2MB~8MB。

3.3.2 缓存一致性域与调度器影响

当调度器将一个进程从 CPU0 迁移到 CPU1 时,该进程的数据在 CPU0 的 L1/L2 缓存中可能处于 Modified 或 Exclusive 状态。硬件一致性协议会自动嗅探(Snoop)或通过目录(Directory)方式将数据迁移到 CPU1 的缓存中。但以下场景需要软件显式维护:

  • 非一致内存访问(DMA):设备访问内存前,需要执行缓存清理(Clean)或失效(Invalidate)操作。
  • 跨集群迁移:在大小核架构(如 big.LITTLE)中,不同集群可能使用不同的一致性协议,调度器迁移时需要额外的内存屏障。
ARM64 多核缓存一致性架构与调度器迁移路径 ARM64 多核缓存一致性架构与调度器迁移路径 CPU0 集群 L1 D-cache L1 I-cache L2 缓存 (统一) CPU1 集群 L1 D-cache L1 I-cache L2 缓存 (统一) 一致性 互连 (ACE/CHI) 主存 (DDR) 调度器进程迁移路径 缓存一致性协议(MESI/MOESI)自动维护迁移数据 Modified → Shared/Invalid 状态转换由硬件嗅探或目录协议完成 软件需在 DMA 或跨集群迁移时显式执行 DC CVAC / IC IVAU 指令

3.4 原子操作与内存屏障指令

调度器是内核中并发度最高的模块之一,大量使用原子操作和内存屏障来保护临界区、实现无锁数据结构。ARM64 提供了丰富的原子指令和屏障指令。

3.4.1 独占访问指令(LDXR/STXR)

ARM64 使用 LDXR(Load Exclusive Register)和 STXR(Store Exclusive Register)实现原子读-改-写操作。调度器中的 atomic_add、atomic_cmpxchg 等操作均基于此实现。

// ARM64 原子加操作示例(内核实现)
static inline void atomic_add(int i, atomic_t *v)
{
    unsigned long tmp;
    int result;

    asm volatile(
"1:    ldxr    %w0, [%2]\n"
"    add    %w0, %w0, %w3\n"
"    stxr    %w1, %w0, [%2]\n"
"    cbnz    %w1, 1b\n"
    : "=&r" (result), "=&r" (tmp)
    : "r" (&v->counter), "Ir" (i)
    : "memory");

    // 说明:
    // 1. ldxr 独占加载 counter 值到 result
    // 2. add 执行加法
    // 3. stxr 尝试独占存储,成功时 tmp=0,失败时 tmp=1
    // 4. cbnz 判断 tmp,非零则跳回 1b 重试
}

3.4.2 内存屏障指令

ARM64 定义了三种主要的内存屏障指令,调度器在不同场景下使用它们来保证内存访问顺序:

指令 全称 屏障类型 调度器典型使用场景
DMB Data Memory Barrier 数据访问顺序 自旋锁、读写锁临界区
DSB Data Synchronization Barrier 数据访问完成 + 等待 TLB 刷新后、缓存维护指令后
ISB Instruction Synchronization Barrier 指令流水线刷新 页表切换后、修改上下文 ID 后
调度器关键屏障序列: 在 context_switch 中,切换 TTBR0_EL1 后必须执行 isb;在 switch_mm 中刷新 TLB 后必须执行 dsb ish + isb。

3.4.3 获取/释放语义(Acquire/Release)

ARM64 在 ARMv8.1 及之后版本引入了 LDAPR 和 STLR 指令,提供硬件级的获取(Acquire)和释放(Release)语义,用于实现更高效的 smp_load_acquire 和 smp_store_release。

// ARM64 获取语义加载(用于无锁队列头指针读取)
static inline int read_head_acquire(struct lockless_queue *q)
{
    int head;

    asm volatile(
"    ldapr    %w0, [%1]\n"
    : "=&r; (head)
    : "r" (&q->head)
    : "memory");

    return head;
}

// ARM64 释放语义存储(用于无锁队列尾指针更新)
static inline void write_tail_release(struct lockless_queue *q, int val)
{
    asm volatile(
"    stlr    %w0, [%1]\n"
    :
    : "r" (val), "r" (&q->tail)
    : "memory");
}
内存屏障在调度器上下文切换中的位置 内存屏障在调度器上下文切换中的位置 context_switch() 入口 switch_mm() → 切换 TTBR0_EL1 DSB ISH + ISB(TLB刷新+同步) switch_to() → 切换栈指针 SP_EL0 屏障作用说明 DSB ISH: 等待所有缓存/ TLB维护操作完成 ISB: 刷新指令流水线 保证后续指令 使用新页表 缺失 DSB/ISB 将导致不可预测 的页表错误

3.5 本章小结