Kvmzen 博客
← 返回技术实践

CPU 如何找到 RAM 中的数据?从虚拟地址、物理地址到 DRAM 地址映射完整解析

AIDevelopment ·约 12 分钟阅读

CPU 如何找到 RAM 中的数据?从虚拟地址、物理地址到 DRAM 地址映射完整解析

症状:你把“虚拟地址、物理地址、DRAM 行列”当成同一个地址,所以看不懂页表、Cache Miss 或内存延迟。
最快解法:先沿着一条 Load 指令分层追踪——虚拟地址 → TLB/页表 → 物理地址 → Cache → 内存控制器 → DRAM;最后一段位映射必须按具体平台核对,不能套固定公式。

这篇文章适合 3 类读者:正在学习操作系统和计算机体系结构的学生;需要分析缓存未命中与内存访问延迟的性能工程师;研究页表、Rowhammer 或虚拟化地址转换的安全开发者。

先把“地址”拆成 3 层

假设程序执行一条读取指令:

value = array[index];

编译器和 CPU 执行这条指令时,程序看到的是一个虚拟地址。它通常由指针、数组基址、索引和元素大小共同计算出来。这个地址属于当前进程的地址空间,不代表某根内存条或某颗 DRAM 芯片上的实际位置。

CPU 中的内存管理单元,也就是 MMU,会把虚拟地址转换成物理地址。Linux 内核文档将物理内存划分为页框,并通过层级页表建立虚拟页到物理页框的映射;页内偏移通常保持不变。(Linux 页表文档)

物理地址仍然不是“DRAM 芯片地址”。它更像是处理器和内存控制器之间使用的线性地址。内存控制器会根据平台设计,把其中一部分位用于选择通道、Rank、Bank、行和列。

层级 CPU 或软件看到的内容 主要作用 是否能直接说明 DRAM 位置
虚拟地址 指针、数组地址、代码中的内存引用 隔离进程、提供连续地址空间 ❌ 不能
物理地址 页框号加页内偏移 连接页表与外部内存系统 ❌ 不能完全说明
DRAM 定位字段 通道、Rank、Bank、行、列等选择信息 让内存控制器发出具体命令 ✅ 但取决于平台实现

这一区分直接解释了一个常见现象:两个进程都可以使用相同的虚拟地址,例如各自都拥有从某个低地址开始的堆区域,但它们的页表可以把这些虚拟页映射到不同的物理页框。操作系统负责建立和切换地址空间,CPU 依据当前地址空间的顶层页表寄存器完成转换。

注意:“地址一样”不等于“数据在同一个地方”。进程隔离、共享内存和写时复制,都是通过不同的页表映射实现的。

虚拟地址怎样进入物理地址转换路径?

以常见的 4 KiB 页为例,虚拟地址可以概念上拆成两部分:

虚拟地址 = 虚拟页号 + 页内偏移

页内偏移用于定位页面内部的字节。若页面大小是 4 KiB,偏移部分需要表示页面内的 4096 个字节位置,因此转换前后这部分通常不变;真正需要查找的是虚拟页号对应的物理页框号。

转换优先从 TLB 开始。TLB 是 CPU 内部用于缓存地址转换结果的硬件结构,可以避免同一虚拟页每次访问都重新遍历页表。Intel 文档明确将 TLB 描述为线性地址到物理地址转换结果的缓存;Linux 文档则将 TLB 和 Page Walk Cache 都列为加速地址翻译的硬件缓存。(Intel 地址转换说明)

当 TLB 命中时,路径可以简化为:

虚拟地址
  ↓
TLB 找到:虚拟页号 → 物理页框号
  ↓
拼接原来的页内偏移
  ↓
得到物理地址

核心并不是把整个地址重新随机计算一遍,而是替换页号部分,再保留页面内部偏移。页大小变化时,页内偏移的长度也会变化;但“页号负责映射、偏移负责定位页内字节”的基本逻辑不变。

如果 TLB 中没有对应条目,CPU 通常会进入硬件页表遍历路径,读取多级页表中的页表项。只有页表项不存在、权限不满足或页面当前不可用时,才会触发缺页异常,由操作系统介入。

TLB 未命中后的处理路径

TLB 未命中后,CPU 需要确认:这个虚拟页是否有合法映射,以及该映射是否允许当前类型的访问。

以多级页表为例,CPU 会使用架构规定的寄存器找到顶层页表。以 x86-64 为例,顶层页表基址由控制寄存器提供;随后,虚拟地址的不同高位字段依次索引各级页表,最后一级页表项给出物理页框相关信息。Linux 页表文档说明,页表是层级结构,顶层页表指针位于寄存器中,虚拟地址高位用于逐级索引,低位用于页内偏移。(Linux 内存管理概念)

页表项通常至少涉及几类信息:

  • 物理页框地址:指出目标页面位于哪个物理页框。
  • Present 或有效状态:判断映射是否存在。
  • 读写权限:阻止只读页面被非法写入。
  • 用户/内核权限:限制用户态访问内核页面。
  • 可执行属性:配合 NX 等机制限制数据页执行。
  • 访问与修改状态:帮助操作系统进行页面回收和写回管理。

因此,TLB 未命中后的结果不只有“找到地址”或“找不到地址”两种:

✅ 页表项有效:得到物理页框,转换结果可写入 TLB。
⚠️ 页面尚未加载:触发缺页处理,系统可能从文件或 Swap 恢复数据。
❌ 权限不符:触发保护异常,例如向只读页面写入。
❌ 地址未映射:通常表现为非法访问或进程崩溃。

Linux 支持不同层级的页表组织,体系结构和配置会影响实际层数;不能把某一款 CPU 上看到的固定级数当成所有系统的通用规律。(Linux 页表层级说明)

页表本身确实存放在内存中,但 CPU 通过架构寄存器定位顶层结构,再按照页表项逐级找到下一级。页表访问也可能经过缓存,处理器还可能使用页表遍历缓存来减少重复读取。

物理地址与 RAM 芯片位置并不等价

物理地址只说明处理器内存系统中的一个地址编号,不直接等于某颗 DRAM 芯片、某个 Bank 或某一行。

一个物理地址经过内存控制器后,可能被拆分为类似这样的字段:

物理地址
  ├─ 通道选择
  ├─ Rank 选择
  ├─ Bank 或 Bank Group 选择
  ├─ 行地址
  └─ 列地址

这里的“拆分”只是概念模型。具体实现可能重新排列地址位,甚至使用地址哈希,让连续物理地址分散到不同通道或 Bank,以提高并行度、降低热点竞争。

所以,Bank、行和列的选择不能通过“物理地址从左到右固定切几位”来回答。你需要同时知道处理器型号、内存控制器实现、主板布线、内存类型、Rank 组织方式,以及平台是否使用地址交错和哈希。

Intel 与 AMD 的架构手册可以确认分页、缓存和内存访问的架构行为,但不会为所有平台提供一套通用的 DRAM 位映射公式。具体映射通常属于平台实现细节,部分内容需要结合厂商资料、固件信息或针对特定平台的研究论文验证。(Intel 64 与 IA-32 架构手册)

经验:如果一篇文章直接给出“物理地址第 12~15 位一定是 Bank、第 16~27 位一定是 Row”,却没有注明 CPU、内存控制器和内存组织方式,通常只能把它当作特定平台示例,不能用于通用推导。

Cache 会改变实际访问路径

物理地址形成后,CPU 通常先检查数据缓存,而不是立刻把请求发送给 DRAM。

一次读取可能经历:

虚拟地址
  ↓
TLB 命中或页表遍历
  ↓
物理地址
  ↓
L1 Data Cache
  ↓ 未命中
L2 Cache
  ↓ 未命中
共享缓存或更低级缓存
  ↓ 未命中
内存控制器
  ↓
DRAM

这意味着你测到的“内存访问延迟”,可能来自完全不同的原因:

  • 地址翻译开销:TLB 命中率低,导致页表遍历。
  • 缓存未命中:数据不在当前缓存层级,需要向更低层请求。
  • DRAM 访问延迟:缓存都未命中,内存控制器需要调度真正的内存请求。
  • 页面错误:数据甚至不在当前物理内存,需要操作系统处理缺页。
  • 竞争和排队:多个核心、设备或线程同时访问内存系统。

因此,不能看到一次慢 Load 就断言“DRAM 响应慢”。性能分析时,你至少要把 TLB Miss、Cache Miss、Page Fault 和内存带宽竞争分开观察。Intel 的优化参考手册也将缓存、预取、内存访问和微架构优化作为不同层面的分析对象。(Intel 优化参考手册)

一条 Load 指令的完整追踪

假设指令需要读取虚拟地址 0x7f... 中的一个整数,你可以按下面 6 步理解:

1.指令生成虚拟地址

CPU 解码 Load 指令,使用基址寄存器、索引寄存器和偏移量计算出虚拟地址。此时 CPU 还没有确认它对应哪个物理页框。

2.检查数据 TLB

CPU 根据虚拟页号查询数据 TLB。命中时,直接得到物理页框号;未命中时,进入硬件页表遍历或相关异常处理路径。

3.检查页表项与权限

页表遍历确认页面存在、权限允许,并将虚拟页号与物理页框号建立对应关系。如果页面不存在,操作系统可能需要分配物理页、读取文件内容或处理 Swap。

你在 Linux 上可以通过内核提供的内存管理工具和进程页表接口观察映射情况,但这些观察结果仍然是操作系统层面的映射,不会自动告诉你 DRAM 控制器内部的 Bank、行和列选择。相关机制可参考 Linux 内存管理文档

4.形成物理地址并检查 Cache

CPU 把物理页框号与页内偏移拼接成物理地址,然后查询缓存层级。若数据已经位于 L1、L2 或共享缓存,数据会直接返回,DRAM 根本不会参与这次读取。

5.内存控制器拆解请求

只有缓存层级都未命中,访问请求才会到达内存控制器。控制器根据物理地址和平台规则选择通道、Rank、Bank、行和列,并决定请求与其他读写请求的调度顺序。

6.DRAM 返回数据并回填

DRAM 完成对应行和列的访问后,数据沿内存控制器、缓存层级返回到执行核心。缓存通常会以缓存行粒度回填,因此一次读取可能把相邻数据一起带入缓存。

如果程序运行在虚拟机中,链路还可能多一层地址转换:

客户机虚拟地址
  ↓
客户机页表
  ↓
客户机物理地址
  ↓
二级地址转换,例如 EPT 或 NPT
  ↓
主机物理地址
  ↓
Cache 与内存控制器
  ↓
DRAM

Intel VT-x 的 EPT 和 AMD-V 的 NPT 都用于虚拟化环境中的第二层地址转换。具体实现、缓存方式和性能影响要以对应架构手册及虚拟化平台文档为准。(Intel 架构手册)

这条链路对性能和安全排查的意义

你可以把问题按指标拆开,而不是笼统地说“内存慢”。

TLB 压力高:优先检查工作集大小、页面布局、页表层级和大页策略。
Cache Miss 高:检查数据结构、访问步长、局部性和线程共享方式。
Page Fault 多:确认是否存在懒分配、文件映射、Swap 或内存压力。
DRAM 带宽接近上限:检查线程数量、读写比例、NUMA 位置和内存访问并发。
⚠️ Rowhammer 研究:需要进一步确认物理页分配、内存刷新和特定 DRAM 组织,不能只凭虚拟地址推断相邻行。
⚠️ 容器或远程开发环境异常:宿主机可用内存、容器限制、Swap 和进程实际工作集可能并不一致。

如果你正在排查 AI Agent、编译任务或远程开发服务的内存占用,建议先阅读 AI Agent 内存占用排查思路,把虚拟内存、进程工作集和系统回收行为分开记录。涉及远程环境时,也应先核对可用内存、并发任务、后台服务和长任务运行状态,再判断问题究竟来自 Cache、页表、Swap 还是实际 DRAM 压力。

当前方案和 Mac 方案的取舍

如果你目前是在普通个人电脑、共享云主机或临时虚拟机上做底层实验,常见缺点是:硬件型号和内存控制器信息不透明;虚拟化会增加第二层地址转换;后台任务可能造成不可控的缓存和带宽竞争;长时间实验还可能受到内存配额、Swap 或宿主机调度影响。

这类环境适合快速验证页表、TLB 和缺页机制,但不适合把某次观察结果直接推广成通用 DRAM 映射结论。若你需要临时获得一台相对独立的 Mac 开发环境,用于编译、性能对比、远程调试或长任务 Agent 测试,租赁 Kvmzen 的 Mac 环境通常比反复调整共享云主机更容易控制变量。你仍应先确认是否需要物理接口、固定硬件拓扑或长期满负载;如果需要,这时自购设备往往更合适。正式选择前,也可以先核对远程访问方式、使用边界和支持范围,避免把虚拟化环境中的观察结果误当成裸机硬件结论。

想继续把体系结构知识落到工程实践,可以从帮助中心中的环境验收与远程使用说明开始;如果需要了解网站主体和服务背景,可参考 Kvmzen 的品牌介绍,再根据任务选择本地运行、云端环境或临时租赁方案。

延伸阅读

限时特惠

不只是一台 Mac,是你在云端的开发基地

独享算力 · 全球节点 · 按月订阅 · 无需购置硬件

返回首页
限时优惠 点击查看套餐