放弃 (3):全局的指令执行顺序

放弃 (3):全局的指令执行顺序

2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

并发程序的状态机模型

状态迁移:选择一个线程执行一条指令

  • Shared memory 会 “立即写入”、“立即读出”
  • 因此存在一个全局的指令执行顺序

2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

但这只是过度简化的幻觉

Reading: Memory Models by Russ Cox

  • 多处理器系统很努力地维护这个幻觉
  • 但这个幻觉是靠不住的 (想象我们在星球之间传递数据)
    • Non-Uniform Memory Access 甚至是分离的核心

center

2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

真实的状态机模型

为了性能:“宽松内存模型” (relaxed memory model)

  • Store 写入 local memory (cache),再慢慢同步给其他处理器
  • 允许 load 读到 local memory (cache) 的旧值

center

2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

一个 “无序” 的真实世界 🌶️

共享内存导致了 “乱序”

  • Store 对其他处理器可见的时间点可能不同

处理器内部也会 “乱序”

  • 对同一个地址的 store/load 允许 bypass
  • 不同地址的 load/store 允许重排
    • “乱序执行” (out-of-order execution): 现代高性能处理器最值得骄傲的特性之一
    • 处理器也是编译器 😂
2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

观测 “无序” 带来的后果 🌶️

int x = 0, y = 0;

void T1() {
  x = 1; int t = y; // Store(x); Load(y)
  __sync_synchronize();
  printf("%d", t);
}

void T2() {
  y = 1; int t = x; // Store(y); Load(x)
  __sync_synchronize();
  printf("%d", t);
}

Model Checker: 01 10 11

  • 实际:00 (????)
2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

观测 “无序” 带来的后果 (cont'd) 🌶️

CPU 设计者面临了难题

  • 更有序的内存模型 = 更糟糕的性能,但更容易编程
  • x86:市面 “最强” 内存模型 (类比 ARM/RISC-V)

center

因此,在 ARM 上模拟 x86 是个世界性的难题

2025 南京大学《操作系统原理》
放弃 (3):全局的指令执行顺序

共享内存 x 地址空间 🌶️

《计算机系统基础》里还学过一个东西叫 TLB

  • Translation Lookaside Buffer
  • 存储了虚拟地址到物理地址的映射
    • 每条指令执行都会访问 TLB
    • (因为 M[PC] 是虚拟机地址)

如果我 munmap/mprotect 改变一段内存?

  • 有另外一个线程在另一个 CPU 上执行
    • 内存都没了,它还在执行?
    • “TLB shootdown”
2025 南京大学《操作系统原理》