物理地址为什么不能连续?--笔记版

4 阅读7分钟

11 物理地址为什么不能连续?--笔记版

下面是一份整理后的学习笔记版本,把前面几轮对话里的核心内容压缩、串起来,便于你以后回顾。


NUMA 架构下:为什么物理地址“不连续”?(学习笔记)

1. 问题背景

在 NUMA(Non-Uniform Memory Access)架构中,经常会看到这样的描述:

  • 不同 NUMA 节点的物理内存地址区间不连续
  • Linux 的“物理内存布局”是分段的、有洞的

疑问是:

  1. 既然都是内存条,为什么不能给它们分配连续的物理地址?
  2. 给内存条分配物理地址,这不就是硬件该干的事情吗?操作系统为什么要面对不连续地址?

2. 核心结论(先给结论)

  1. 物理地址是硬件定义和解释的,不是操作系统“想怎么编就怎么编”的。
  2. 在 NUMA 架构下,物理地址不仅仅是“门牌号”,而是“导航坐标”
    物理地址 = 节点ID(Node ID) + 节点内偏移(Offset)
  3. 硬件把 “节点ID”编码在物理地址的高位 bit 中,用来做快速路由/译码
    结果就是:整体上看,物理地址空间必然是分段的、不连续的
  4. 不是“不能”分配连续,而是如果强行设计为连续,会让硬件寻址逻辑变复杂、变慢,违背 NUMA 想要的高性能目标

3. 从传统 UMA 到 NUMA:设计思路的变化

3.1 传统 UMA(非 NUMA)模型

  • 所有内存条挂在一个统一的内存控制器下面(类似早期北桥)。
  • 这个统一控制器看到的是一片“大平原”:
    • 物理地址从 0 一直递增到 Max连续
  • CPU 访问内存时:
    • 把物理地址放到总线上;
    • 内存控制器负责:
      • 接收这个地址;
      • 决定落在哪根内存条、哪一行/列。

整体就像有一个**“总管理员”**,管理整个大楼所有房间,房间号连续,任何请求都先找他。

问题:随着核心数、内存容量暴增,“总管理员”成为瓶颈 → 扩展性和性能受限。


3.2 NUMA 模型:多节点 + 本地内存

NUMA 的核心思想:

  • 把系统分成多个 节点(Node)
    • 每个节点有:
      • 自己的 CPU 核/Socket
      • 自己的内存控制器
      • 自己直连的一块物理内存(本地内存)
  • 本地访问(本节点 CPU → 本节点内存)非常快;
  • 远程访问(跨节点)通过互连总线,延迟更高。

类比:

  • 过去是一个大楼一个总管理员
  • 现在是每栋楼一个管理员,每栋楼有自己的独立房号系统

4. 关键点:物理地址是如何被“解码”和“路由”的?

4.1 地址不是“流水号”,而是“编码”

在 NUMA 中,一个物理地址在硬件眼里是类似:

高位 bits:要去哪个节点(Node ID)
低位 bits:在该节点内的偏移(Offset)

就像电话号码:

  • 国家码 + 区号 + 本地号码
  • 比如:+86 - 10 - 88888888

对应到 NUMA:

  • Node ID 就像“区号”
  • Node 内偏移 就像“本地号码”

CPU 发出的“物理地址”本质上是:
“去某个节点的某个偏移位置”


4.2 硬件如何“扳道岔”

当 CPU 发出一个物理地址时:

  1. 地址信号上线(address bus)上。
  2. 硬件地址译码电路查看物理地址的高位 bit
    • 比如最高 1~n 位决定这是 Node 0、Node 1、Node 2……
  3. 电路像扳道岔一样:
    • 如果高位位型表示 Node 0 → 信号路由到 Node 0 的内存控制器;
    • 高位表示 Node 1 → 路由到 Node 1;
    • ……
  4. 节点内存控制器拿到“属于自己的那部分地址”,再用低位 bits在本节点内存条上选择行/列。

重要的是:

  • 这个“按高位分发”的机制,是硬件固定逻辑。
  • 为了让这个逻辑极度简单 & 极快,一般不会做复杂比较/查表,而是用位模式划分地址段。

5. 为什么地址必然“不连续”?

用一个典型的二进制划分的思路:

  • 设想系统用物理地址最高 1 位作为节点选择位:
    • 高位为 0 → Node 0
    • 高位为 1 → Node 1

那么:

  • Node 0 的地址范围天然是:
    • 0x0000...<最高位为0的最大值>,例如 0x7FFF FFFF FFFF
  • Node 1 的地址范围天然是:
    • 0x8000 0000 00000xFFFF FFFF FFFF

此时,从数轴上看:

  • Node 0 的最大地址:0x7FFF FFFF FFFF
  • Node 1 的起始地址:0x8000 0000 0000

中间有一个巨大跳跃,数值上不连续,但这是硬件逻辑最简单、最快速的一种划分方式:

  • 只看最高位是 0 还是 1,就立刻决定发往哪个节点。
  • 不需要:
    • “如果 Address ≥ 某个值就去 Node 1”
    • 或“如果在某个表中落在哪一段就去某个 Node”
  • 逻辑门数量少,延迟低,性能稳定。

如果有多个节点,就用更多高位 bits 组合成不同的节点 ID,划分成更多不相邻的地址段。

结论
硬件用“地址高位编码 Node ID”的方式实现快速寻址与分发
从而导致全局物理地址空间在数值上必然呈现为分段、不连续


6. “不能连续分配”到底是什么意思?

注意区分两个层次:

  1. 硬件层面

    • 在电路设计/布线时,就已经规定好了:
      • 哪些地址高位模式 → 哪个节点。
    • 这决定了物理地址 → 节点的映射关系。
    • 这不是 OS 运行时可以修改的,而是固化在 CPU + 芯片组 + 内存控制器设计里
  2. 操作系统层面

    • OS 启动时,读取 ACPI/firmware 提供的内存映射表(E820、SRAT 等):
      • 得到:哪些物理地址范围属于哪个 NUMA 节点。
    • OS 只能在硬件给定的这些区间内管理内存
    • 它“看到”的物理内存布局自然是:
      • 一段属于 Node 0
      • 一段属于 Node 1
      • 中间可能还夹着保留区、设备 MMIO 区等
        → 因此呈现为不连续的 PFN 区间

所以:

  • “物理地址为什么不能连续?”
    不是说“软件不愿意连续分配”,而是
    硬件直接把 Node 信息编码在地址里,导致从数值上看地址天生是分块的。

  • OS 只是服从这个事实,在此基础上做:

    • NUMA-aware 内存分配(尽量本地分配)
    • 页迁移、内存策略(绑核绑内存)

7. 再用一个直观类比:邮编 & 小区地址

可以把物理地址类比成“邮寄地址”:

  • 邮政编码 = Node ID(节点 ID)
  • 街道门牌号 = 节点内偏移(Offset)

全国地址整体来看,肯定不是 1、2、3、4……这样的连续整数:

  • 北京市…(邮编 100000) 一批;
  • 上海市…(邮编 200000) 一批;
  • 中间邮编从 100000 跳到 200000,看起来是不“连续”的。

但这种不连续,正是为了:

  • 投递效率高
  • 按邮编快速路由邮件

NUMA 中也是一样:

  • 地址高位 bits 像邮编,硬件一看就知道该丢给哪个节点的内存控制器
  • 地址低位 bits 像门牌,在该节点内定位具体物理单元。

8. 小结

用一句话收束整个逻辑:

在 NUMA 架构中,物理地址不是纯粹的“线性编号”,而是“节点ID + 节点内偏移”的编码。
节点 ID 被编码在物理地址的高位,用于硬件快速决定信号路由到哪个内存控制器。
这种设计带来高性能和可扩展性,但从数轴上看,物理地址空间必然是分段的、不连续的。
分配地址的“根本决定权”在硬件/固件,操作系统只是按这个不连续布局进行管理和优化。

如果你愿意,我可以在这个笔记基础上,再补一张示意图的文字描述(比如:两个节点的地址区间、译码流程、访问本地/远程内存的路径),方便你自己画在纸上或做成 PPT。