共享内存多处理器的基本概念

5.7.1 把 MIMD 分成了两支:共享内存和消息传递。这一节讲第一支——也是 408 唯一要求展开的那一支。

定义只有一句:

共享内存多处理器(SMP,Shared-Memory Multiprocessor):所有处理器共享同一个物理地址空间,同一个地址在任何处理器上都指向同一个存储单元。

这一句里最要紧的是后半句。 它带来了两个好处和两个麻烦,本节其余部分全在处理这四件事:

内容去哪解决
好处一通信只需读写变量,不必显式收发消息——
好处二程序模型接近单机,易于移植——
麻烦一访问速度可能不均匀 → UMA / NUMA本节
麻烦二私有 Cache 里的副本可能不一致 → Cache 一致性本节 + 3.5.5

机制

一、UMA 与 NUMA

按”各处理器访问内存的时间是否相同”划分。

UMA(均匀存储访问)NUMA(非均匀存储访问)
访问延迟所有处理器访问任何内存单元时间相同访问本地内存快,访问远程内存慢
结构处理器与内存之间是对称的(总线或交叉开关)内存分散挂在各处理器上,通过互连网络互访
扩展性差——总线是瓶颈,处理器一多就争不过来好——本地访问不上互连网络
典型单芯片多核、小规模双路系统大型多路服务器、多插槽系统

NUMA 的地址空间仍然是统一的——这一点极容易搞错。“非均匀”说的是访问时间,不是地址空间。 每个处理器都能用同一个地址访问到同一个单元,只是有的快有的慢。

仍是共享内存;不共享地址空间的那种叫多计算机

NUMA 对操作系统提出了新要求:调度时应尽量让线程跑在它的数据所在的那个结点上,内存分配也应优先分配本地内存。这又是一处”硬件特性必须由 OS 配合才能生效”的例子,和 5.7.2 超线程需要 OS 知道拓扑是同一类问题。见 OS 2.2.6。

SMP(对称多处理器)通常指 UMA 结构:各处理器地位对等,都能访问全部内存和 I/O,操作系统只有一份、任一处理器都能执行内核代码。

与之相对的是 AMP(非对称多处理器):处理器地位不对等,有主从之分——这个词现在少用,但”大小核”某种意义上是它的回归。

二、Cache 一致性问题

问题从哪来:多核各有私有 L1/L2(5.7.3),同一个内存单元的副本可能同时躺在几个核的 Cache 里。

初始:内存 x = 0,核心 A 和 B 的 Cache 里都缓存了 x = 0

核心 A:x = 1        → A 的 Cache 里 x = 1
核心 B:读 x         → B 的 Cache 里还是 0  ← 读到了旧值

这就是一致性问题:同一个地址的多个副本不一致。

注意它是多核特有的问题——单核不会出现,因为只有一份 Cache。3.5.5 已经讨论过写策略(写直达/写回、写分配/非写分配),那里解决的是”Cache 与主存之间”的一致,这里解决的是”Cache 与 Cache 之间”的一致。

两类协议

监听(侦听)协议 Snooping目录协议 Directory
怎么工作每个 Cache 监听总线上的所有事务,发现别人写了自己缓存的地址就作出反应用一个目录集中记录”每个块被哪些 Cache 缓存了”,写时只通知这些 Cache
依赖共享总线(广播)互连网络,不需要广播
扩展性差——总线带宽是瓶颈好
适合小规模(多核芯片内)大规模(NUMA 多路系统)

两者的取舍就是”广播 vs 点播”:监听协议简单但每次写都要打扰所有人,目录协议要维护一张表但只通知相关的几个。

两种处理方式

写失效(Write Invalidate)写更新(Write Update)
做法我要写,先让别人的副本全部失效我写了,把新值广播给所有持有副本的 Cache
总线流量只在第一次写时有一次失效广播每次写都要广播新值
主流✅ 是主流少用

写失效之所以成为主流:一个变量常被同一个核连续写多次,写失效只在第一次付出代价,之后这个块已被独占,后续写不再打扰任何人。写更新则每次都要广播。

三、Cache 一致性 ≠ 存储一致性

这是本节最容易失分的一处辨析,也是最值得想清楚的一处。

Cache 一致性(Coherence)存储一致性(Consistency)
管的是同一个地址的多个副本不同地址的访问在各处理器眼中的顺序
保证什么对同一个地址,所有处理器最终看到同一个值一个处理器的多次写,在别的处理器看来是什么顺序
靠什么一致性协议(监听 / 目录)存储一致性模型 + 内存屏障指令

举例说明第二个为什么是独立的问题:

核心 A:            核心 B:
  x = 1;             while (flag == 0) ;
  flag = 1;          print(x);       ← 会不会打印出 0?

即使 Cache 一致性完美工作(B 读 flag 一定能读到 1,读 x 也一定能读到最新值),B 仍可能打印 0——因为处理器可能把 A 的两条写调换顺序执行(乱序执行),flag = 1 先于 x = 1 生效。

一致性协议只保证”每个地址各自的副本一致”,不保证”不同地址的写按程序顺序对外可见”。 后者由存储一致性模型规定,需要内存屏障指令来强制。

一致性同一地址的值;存储一致性不同地址的序

四、一致性不等于互斥

这是第二个高频陷阱,而且它直接连到操作系统。

Cache 一致性保证你读到的是最新值,但不保证”读-改-写”这个序列不被打断。

两个核同时执行: count = count + 1;

核 A:读 count(=5)        核 B:读 count(=5)
核 A:算 5+1=6              核 B:算 5+1=6
核 A:写 count=6            核 B:写 count=6

结果:count = 6,而不是 7

每一步读到的都是”最新值”,一致性协议毫无问题——但结果还是错的。

互斥需要的是原子性,而原子性靠专门的硬件指令提供:

指令作用
TAS(Test-and-Set)读出旧值并写入 1,这两步不可分割
CAS(Compare-and-Swap)值等于期望值时才写入,比较和写入不可分割
LL/SC(Load-Linked / Store-Conditional)中间若有别人写过就失败重来

这些指令是 OS 2.3.3 互斥锁和信号量能实现的硬件基础——操作系统所有的同步原语,最终都落在这几条指令上。

所以三个层次要分清:

层次解决什么谁提供
Cache 一致性同一地址的副本一致硬件协议,自动
原子指令读-改-写不可分割硬件指令,要显式使用
互斥 / 同步临界区互斥、条件等待操作系统用原子指令实现

下层不能替代上层。 有了一致性不代表有原子性,有了原子指令不代表程序自动正确。

五、共享内存 vs 消息传递:回顾

这一节结束时,把 5.7.1 那张表补完整:

共享内存多处理器多计算机(消息传递)
地址空间单一各自独立
通信读写共享变量(隐式)send / recv(显式)
需要一致性协议✅❌
需要同步原语✅通信本身即同步
编程难度较易,但易出并发 bug较难,但bug 更显式
扩展规模数十到数百核数千到数万结点
内部再分UMA / NUMA——

“共享内存好写但容易错、消息传递难写但错得明显”,这个取舍在分布式系统里一直存在。

边界

边界辨析:UMA / NUMA

  • UMA:访问任何内存单元的时间相同;扩展性差
  • NUMA:访问本地快、远程慢;扩展性好

NUMA 仍然是共享内存、仍然是统一的地址空间——“非均匀”指的是时间不是空间。

边界辨析:Cache 一致性 / 存储一致性

  • Cache 一致性:同一个地址的多个副本的值要一致
  • 存储一致性:不同地址的访问在各处理器看来的顺序

一致性协议不能保证跨地址的顺序,那需要内存屏障。

边界辨析:一致性 / 互斥

Cache 一致性不提供互斥。 它保证每次读到最新值,但**“读-改-写”仍可能被打断**。

互斥需要 TAS / CAS / LL-SC 这类原子指令,操作系统再用它们实现锁(OS 2.3.3)。

边界辨析:写失效 / 写更新

  • 写失效:让别人的副本失效。只有第一次写付出代价,主流做法
  • 写更新:广播新值。每次写都有开销

一道题问”哪种总线流量小”,答写失效。

边界辨析:监听协议 / 目录协议

  • 监听:靠总线广播,简单但扩展性差,用于小规模
  • 目录:集中记录谁缓存了什么,只点播,用于大规模 NUMA

对照速查

说法判断
NUMA 的各处理器有各自独立的地址空间❌ 地址空间统一,只是访问时间不同
SMP 一般指 UMA 结构✅
Cache 一致性问题只出现在多处理器系统✅ 单核只有一份 Cache
写失效协议的总线流量小于写更新✅
监听协议适合大规模系统❌ 总线是瓶颈,适合小规模
Cache 一致性能保证临界区互斥❌ 需要原子指令
Cache 一致性等同于存储一致性❌ 一个管值一个管序
消息传递系统需要 Cache 一致性协议❌ 地址空间独立,不存在共享副本
SMP 一般采用偶数路 CPU✅ 按王道口径
层次解决提供者
Cache 一致性同址副本一致硬件,自动
原子指令读-改-写不可分割硬件,显式使用
锁 / 信号量临界区互斥操作系统

考点

  • UMA / NUMA 辨析:NUMA 的地址空间是统一的,这是最常见的误判。
  • Cache 一致性 vs 存储一致性:一个管值、一个管序。
  • 一致性不提供互斥:高频陷阱,要能说出”读-改-写会被打断”。
  • 写失效 vs 写更新:写失效流量小、是主流。
  • 监听 vs 目录:监听靠广播适合小规模,目录适合大规模。
  • 共享内存 vs 消息传递:判据是”同一地址是否同一单元”。
  • SMP 偶数路:按王道口径记,同时分清”路 ≠ 核”(5.7.3)。

链接