《计算机体系结构:量化研究方法》第二章——虚拟存储器

Explain

阅读该文章,需要对"虚拟内存"有一个基本的掌握,对于该部分内容的总结,可参考《深入理解计算机系统》第九章------虚拟内存

一、虚拟存储器

1.1 虚拟存储器回顾

1)物理空间的共享与保护

现代计算机在运行过程中,通常会有多个进程同时执行,且每个进程都拥有独立的地址空间。如果为每个进程都分配完整的物理内存空间,成本将不可接受,更何况大多数进程仅会使用其地址空间的一小部分。因此,必须建立一种机制,使多个进程能够高效、安全地共享有限的物理内存。

虚拟存储器(Virtual Memory)便是这一问题的核心解决方案。它将物理存储器划分为若干块并分配给各进程,同时通过严格的保护机制限制进程的访问权限,确保其仅能访问所属的内存块。此外,虚拟存储器还通过"按需加载"提升了程序的启动速度,因为程序在运行初初期不再需要将所有代码和数据全部载入物理内存。

2)从手动"覆盖"到自动管理

尽管保护机制在现代计算中至关重要,但共享并非发明虚拟存储器的初衷。在虚拟存储器出现之前,若程序体积超过物理内存上限,程序员必须手动将其划分为互斥的覆盖段(Overlay),并在程序运行期间根据逻辑需要手动加载或卸载这些片段。

程序员必须精确确保程序不会访问超出物理边界的地址,并负责触发正确的加载时机。这种繁琐的任务极大地降低了开发效率。虚拟存储器的诞生彻底减轻了这一负担:它实现了主存储器(DRAM)与辅助存储器(磁盘)之间两级层次结构的自动化管理

下图展示了一个示例,其中程序的虚拟地址空间被映射到物理存储器中,共包含A、B、C 和 D 4 个页面。这 4 个页面对应的实际位置,A、B、C 在物理主存中,D 在物理磁盘上。

除了提供受保护的共享存储空间以及自动管理存储器层次结构之外,虚拟存储器还显著简化了程序加载与执行过程。这一机制被称为再定位(relocation),它允许同一程序在物理存储器中的任意位置运行。如上图所示,程序既可以被放置在物理存储器的不同位置,也可以暂存在磁盘上,系统只需相应地调整虚拟地址与物理地址之间的映射关系即可。

3.)虚拟存储器与缓存(Cache)的异同

从存储层次结构的角度看,虚拟存储器的基本思想与第 1 章所述的缓存机制非常相似,但在术语体系上存在差异:

  • 块(Block) :在虚拟存储器中称为页(Page)段(Segment)
  • 缺失(Miss) :称为页错误)(Page Fault或地址错误。

处理器发出虚拟地址 ,通过软硬件协同构建的存储器映射 (地址变换)机制转化为物理地址,进而访问物理内存。目前的二级存储层次结构通常由 DRAM(主存)和磁盘(辅存)组成。下表是缓存与虚拟存储器的典型参数对比:

参数 第一级缓存 (L1 Cache) 虚拟存储器 (VM)
块/页大小 16 -- 128 字节 4096 -- 65,536 字节
命中时间 1 -- 3 个时钟周期 100 -- 200 个时钟周期
缺失代价 8 -- 200 个时钟周期 1,000,000 -- 10,000,000 个时钟周期
缺失率 0.1% -- 10% 0.00001% -- 0.001%
地址映射 物理地址 缓存地址 虚拟地址 物理地址

关键差异:

  1. 控制主体:由于页错误代价极高(磁盘访问极慢),虚拟存储器的块替换由操作系统主导,以利用更复杂的算法减少缺失;而缓存缺失完全由硬件处理。
  2. 空间规模:虚拟存储器的大小由处理器地址位数决定,而缓存容量则由硬件成本和物理设计限制。

4)分页与分段:两种实现路径

虚拟存储器系统主要分为两类:分页(Paging)分段(Segmentation)

  • 分页:采用固定大小的块(通常为 4KB - 8KB),易于管理和替换。
  • 分段:采用可变大小的块(从 1 字节到 字节),符合程序的逻辑结构。

下表是分页与分段的深度对比:

特性 分页 (Paging) 分段 (Segmentation)
地址表示 单一字(页号 + 偏移) 两个字段(段号 + 段内偏移)
透明度 对程序员不可见 程序员可见其逻辑划分
替换难度 极易(块大小统一) 困难(需寻找足够大的连续空闲空间)
碎片问题 内部碎片(页末尾浪费) 外部碎片(段间难以利用的小空隙)
磁盘通信 高效(可针对页大小优化传输) 不稳定(小段传输效率极低)

趋势:页式分段(Paged Segmentation)

为了结合两者的优势,现代系统常采用"页式分段":段由整数个页组成。这样既保持了逻辑上的分段结构,又在物理层面利用分页简化了替换过程。此外,现代处理器(如 IBM 405CR)开始支持多级页大小(从 1KB 到 4MB 不等),以灵活应对不同的应用需求。

1.2 主存储器中的4个问题

问题一:一个块(行)可以放在主存储器中的什么位置?

在存储层次结构中,缺失代价(Miss Penalty)决定了放置策略的灵活性

  • 设计逻辑:由于虚拟存储器的"底层"是访问延迟极高的磁盘(毫秒级),一次缺页就会导致处理器停顿数百万个周期。为了最大程度降低这种灾难性的缺失率,系统必须利用好每一寸主存空间。
  • 策略选择 :操作系统采用全相联(Fully Associative)映射。这意味着任何一个虚拟页(Virtual Page)都可以映射到物理内存中的任何一个物理页帧(Page Frame)。
  • 收益:全相联消除了"冲突缺失(Conflict Miss)",只有当物理内存完全填满时才会发生容量缺失。这种灵活性虽然增加了查找的复杂性,但在巨大的缺失代价面前,这种交换是绝对值得的。

问题二:如果一个块就在主存储器中,如何找到它?

定位物理内存中的数据主要依赖于地址翻译(Address Translation)机制。

1. 分页机制(Paging)与地址拼接

在分页系统中,虚拟地址被分为虚拟页号(VPN)页内偏移量(Offset)

  • 翻译过程 :VPN 作为索引查询页表,获取物理页框号(PPN)
  • 拼接:由于页的大小是 2 的幂,偏移量在物理地址中保持不变。物理地址最终由 PPN 和 Offset 直接拼接而成。
2. 页表的规模与反转分页表

对于 32 位地址空间、每个页面 4KB 且 PTE 为 4 字节 的系统:
页表大小 = 2 32 2 12 × 2 2 = 2 22 字节 = 4 MB \text{页表大小} = \frac{2^{32}}{2^{12}} \times 2^2 = 2^{22}\ \text{字节} = 4\ \text{MB} 页表大小=212232×22=222 字节=4 MB

在 64 位系统中,这个数值将呈指数级增长。

这类页表在大地址空间系统中会占用相当可观的存储空间。为减小这一开销,一些计算机体系结构对虚拟地址应用散列(hashing)机制 ,从而使页表的大小与物理页的数量成正比,而不再与虚拟页的数量成正比。

这种结构被称为反转分页表(inverted page table)。在前述示例中,如果物理存储器容量为 512 MB,则仅需:

512 MB / 4 KB × 8 字节 ≈ 1 MB 512\ \text{MB} / 4\ \text{KB} \times 8\ \text{字节} \approx 1\ \text{MB} 512 MB/4 KB×8 字节≈1 MB

即可存储反转分页表,其中每个表项通常还需要额外的字段(例如 4 字节)来记录对应的虚拟页号。HP/Intel IA-64 架构同时支持传统页表和反转分页表,具体采用哪种机制由操作系统设计者自行决定。

由于逐次访问页表会显著增加地址转换延迟,现代处理器通常配备一个专门用于缓存地址转换结果的小型高速缓存,称为变换旁视缓冲区(Translation Lookaside Buffer,TLB)。TLB 能够在绝大多数情况下直接提供虚拟页号到物理页号的映射,从而显著降低地址转换开销。关于 TLB 的结构与工作机制,将在后续内容中进一步介绍。

问题三:在虚拟存储器缺失时应当替换哪个块?

缺页异常(Page Fault)发生时,选择"牺牲者"的目标只有一个:最小化未来的缺失率。

  • 算法选择:最近最少使用(LRU)
    基于程序访问的时间局部性,过去很长一段时间没被访问的页,未来被访问的概率也最低。
  • 硬件辅助:使用位(Use Bit)
    为了让 LRU 算法更具可行性,硬件为每个页维护一个"使用位"。从逻辑上讲,每当某个页被访问时,其对应的使用位就应被置位。
  • 更新机制 :为了性能,硬件通常仅在 TLB 缺失页访问时置位。
  • 操作系统行为:内核会周期性地扫描并清零这些位。如果一个位在一段时间后仍为 0,说明它是替换的理想候选者。

问题四:在写入时发生什么?

由于 DRAM 和磁盘之间的速度鸿沟,虚拟存储器的写入策略没有悬念。

  • 唯一选择:写回策略(Write-back)
  • 理由:磁盘写入延迟太高。如果采用"直写(Write-through)",处理器将在每次写指令时停顿数毫秒,系统将完全丧失可用性。

为了避免不必要的磁盘写入,虚拟存储器系统通常为每个页维护一个重写位(dirty bit,或 modified bit)。该位用于指示该页自从上一次从磁盘调入主存储器之后,是否被修改过。只有当一个页在被替换时,其重写位被置位,操作系统才需要将该页的内容写回磁盘;如果该页自调入以来未被修改,则可以直接丢弃而无需任何磁盘写操作。

这种机制显著减少了对后援存储的访问次数,从而在保证正确性的同时,大幅降低了虚拟存储器系统的整体开销。

1.3 快速地址变换技术

在虚拟存储器体系中,由于页表通常规模庞大,必须存储在主存(DRAM)中,甚至可能需要多级分页管理。这导致逻辑上的每一次存储器访问都面临严重的性能损失:

  • 第一阶段:访问主存查阅页表,将虚拟地址转换为物理地址。
  • 第二阶段:再次访问主存,根据物理地址获取实际数据。

这种"双重访存"会将访问延迟直接翻倍。为了化解这一矛盾,系统利用局部性原理 ,将最近使用的地址映射关系缓存在一个专用的高速硬件结构中------变换旁视缓冲区(TLB)。借此,绝大多数访存请求只需在 TLB 内部完成瞬间转换,极大地提升了执行效率。

TLB 的每一项在逻辑上类似于一个高速缓存块(Cache Block),由两部分组成:

  • 标志字段(Tag):存储部分虚拟地址,用于匹配访存请求。
  • 数据字段(Data) :核心内容是物理页帧号(PPN),此外还包含一系列控制位:
  • 保护字段 (读/写/执行权限)、有效位使用位 (辅助替换算法)以及重写位(即脏位,标识页面是否被修改)。

重要提示:一致性管理

当操作系统修改页表项(PTE)的物理映射或权限属性时,必须同步使 TLB 中对应的项失效。如果 TLB 中残留了过期的映射信息,处理器将继续使用错误的物理地址或过时的权限,导致程序行为异常或系统安全违例。

下图展示了 Opteron 处理器中数据 TLB 的组织方式,并标示了地址变换的各个步骤。

以 Opteron 处理器的全相联数据 TLB 为例,其地址变换过程可划分为四个高效步骤:

  1. 并行匹配(步骤 1 & 2):由于采用全相联结构,虚拟地址会同时与 TLB 内的所有项进行比较。只有标记为"有效"的项才会参与匹配。
  2. 安全性检查:在匹配的同时,硬件会依据保护字段检查当前访问(读/写/指令提取)是否合法。
  3. 结果选出(步骤 3) :一旦标志匹配成功,通过一个 40 选 1 的多路选择器(MUX),从 TLB 数组中迅速提取出对应的物理页帧号。
  4. 物理地址合成(步骤 4) :由于页内偏移量(通常为低 12 位)在虚拟和物理地址中是完全一致的,系统只需将提取出的物理页帧号与原始虚拟地址中的偏移量进行拼接。在 Opteron 中,这一过程最终生成一个 40 位的完整物理地址。

由于地址变换处于访存的关键路径上,其延迟直接决定了处理器的时钟频率。为了在不牺牲速度的前提下保证缓存一致性,Opteron 的一级缓存(L1 Cache)采用了虚拟索引、物理标记(Virtually Indexed, Physically Tagged, VIPT)设计。

这种设计的精妙之处在于:它允许缓存索引与 TLB 地址变换同步进行。当 TLB 正在拼命转换地址时,缓存已经根据虚拟地址的部分位定位到了可能的块组,一旦 TLB 输出物理标记,缓存立即进行比对。这种并行操作最大程度地降低了指令流水线的停顿。

1.4 选择页大小

页大小是虚拟存储器设计中最关键的体系结构参数之一。选择页大小时,设计师必须在硬件效率 (偏向大页)与资源利用率(偏向小页)两种对立的力量之间寻找平衡。

采用较大页面(Large Pages)的主要优势在于降低管理开销和提升传输性能:

  • 减少页表占用空间:页表的大小与页大小成反比。增大页面可以显著减少页表项(PTE)的数量,从而节省主存资源。
  • 优化缓存设计:如 B.3 节所述,较大的页面偏移量有助于设计容量更大、且能实现"虚索引物理标签(VIPT)"的快速缓存,从而缩短命中时间。
  • 提高 I/O 传输效率:无论是通过磁盘还是网络,大块数据的传输效率远高于小块数据。大页能更好地平摊存储设备的寻道时间和通信协议的开销。
  • 扩大 TLB 覆盖范围(Reach):TLB(快表)的条目数量通常非常有限。增大页大小意味着在条目数不变的情况下,TLB 能够映射更广的内存区域,从而大幅降低 TLB 缺失率。

由于 TLB 缺失对 CPI(每条指令时钟周期)的影响有时甚至不亚于缓存缺失,现代处理器普遍支持多种页大小(如同时支持 4KB 和 2MB/1GB 的"巨页")。

支持较小页面(Small Pages)的核心目的在于节省物理空间和提高灵活性:

  • 减少内部碎片(Internal Fragmentation) :当进程的内存需求(如代码段、堆或栈)不是页大小的整数倍时,页内未使用的空间即为内部碎片。
    量化分析:假设每个进程拥有三个主要段(文本、堆、栈),平均每个进程浪费的存储量约为 1.5 倍页大小。在拥有数 GB 内存的现代计算机中,4 KB 或 8 KB 的浪费微乎其微;但若页大小超过 32 KB,内存和 I/O 带宽的浪费将变得不容忽视。
  • 优化进程启动时间:许多小型进程的总数据量较少。若页大小过大,启动进程时加载整个页面会产生不必要的延迟。采用较小页面可以实现更精准的"按需分页(Demand Paging)"。

1.5 虚拟存储器和缓存小结

下图给出了一个假设示例:从 64 位虚拟地址映射到 41 位物理地址,系统包含两级缓存。L1 缓存的容量与页大小均为 8 KB,因此采用虚拟寻址、物理标记的方式;L2 缓存容量为 4 MB。两级缓存的块大小均为 64 字节。页大小为 8 KB,TLB 为直接映射,有 256 项。

当处理器发起一个访存请求时,它手中持有的是 64 位虚拟地址。这个地址被逻辑上分为两部分:

  • 页偏移量(Page Offset):13 位(因为 8 KB = 2 13 2^{13} 213 字节),这部分在虚拟地址和物理地址中是完全相同的。
  • 虚拟页号(VPN):剩余的 51 位(64 − 13)。

接着并行 开始 TLB 查找L1 索引提取

  • TLB 查找:VPN([63:13])被送往 TLB,TLB 的任务是将其变换为物理页号(PPN)。
  • L1 索引提取:与此同时,页偏移量的高位被提取作为 L1 缓存索引。
    由于 L1 是 8 KB 的直接映射缓存,且块大小为 64 字节,它共有8 KB / 64 B = 128( 2 7 2^{7} 27)个行。
    因此,索引需要 7 位:地址 [12:6] 作为索引,[5:0] 作为块内偏移。

然后进入标签比对(Tag Match)阶段:

  • 一旦 TLB 命中,输出 28 位的 物理地址页号PPN(41 − 13)。L1 缓存将该 PPN 与通过索引找到的缓存行中的"标志(Tag)"进行比对。
  • 若标志匹配且有效,则发生 L1 命中,直接返回数据。

如果 L1 缺失,系统将使用合成好的 41 位物理地址去查询 L2 缓存。

  • 物理地址位分配(L2 为 4 MB):
    块偏移量(Block Offset) :[5:0](6 位,对应 64 字节块);
    L2 索引(Index) :[21:6](16 位),因为 4 MB / 64 B = 65,536( 2 16 2^{16} 216)个行;
    L2 标志(Tag):剩余的高位 [40:22](19 位)。

  • 访问过程:

    硬件使用物理地址的第 6 到第 21 位定位 L2 缓存行;

    将该行的 Tag 与物理地址的高 19 位进行比对;

    如果再次缺失,则发起代价高昂的主存(DRAM)访问。

二、虚拟存储器的保护与示例

在多重编程环境中,多 个程序并行运行并共享同一台计算机。这种运行方式对"进程之间如何隔离、如何共享"提出了新的要求,而这些正是虚拟存储器机制要解决的核心问题。

多重编程引入了"进程"的概念:进程不仅是程序本身,还包括维持其继续运行所需的全部状态。时分共享系统允许多个用户交互式地使用同一台计算机,使每个用户都感觉自己独占资源。为此,系统必须能够在任意时刻从一个进程切换到另一个进程,这称为进程切换上下文切换

无论进程是连续执行还是被频繁中断,其行为都必须保持正确。为此:

  • 体系结构必须支持保存与恢复进程的处理器状态;
  • 操作系统必须防止进程之间相互干扰。

理论上,最安全的方式是在每次切换时将进程状态写回磁盘,但这种代价过高,无法满足时分共享的需求。实际做法是:由操作系统将主存划分给多个进程,使它们同时驻留在内存中。为保证安全,硬件必须提供保护机制,确保

  • 一个进程不能访问或修改其他进程的存储区域。
  • 同时又允许在受控条件下共享代码和数据,以支持通信并减少冗余。

虚拟存储器正是实现这种"隔离与共享"的基础设施。

2.1 保护进程(独立页表)

最基本的保护方式,是为每个进程维护独立的页表,将其虚拟地址空间映射到不同的物理页面。这样,不同进程即使使用相同的虚拟地址,也会被映射到不同的物理位置,从而实现隔离。

必须防止用户程序篡改页表或绕过保护机制,因此:

  • 页表只能由内核修改;
  • 普通程序无法伪造映射关系。

在更复杂的系统中,保护机制可从简单的"用户态 / 内核态"两级模型扩展为多级结构。处理器通过"环(Ring)"将权限划分为多个层级:内层权限最高,外层逐级降低。普通应用位于最外层,受到最严格的限制。除读写权限外,还可以控制代码是否可执行,以及跨级调用的入口。

在实际系统中,多环结构相较于二级模型的差异并不总是显著。但在高安全场景下,仅靠分级已不足够,于是引入"钥匙---锁"式的能力模型:程序只有持有相应"钥匙"才能访问资源。为保证安全,这些能力必须由硬件与操作系统受控传递,且不可伪造。

Intel 80x86 体系结构在演进过程中尝试过多种保护模型。由于强调向后兼容,它保留了这些探索的成果。后续将回顾其中两种代表性方案:早期的分段地址空间,以及较新的平坦 64 位地址空间模型。

2.2 分段虚拟存储器举例

这部分内容了解即可,现在主流处理器都使用分页虚拟存储器。

最早的 8086 采用分段寻址,但既不支持虚拟存储,也没有任何保护机制。分段只有基址寄存器,没有界限寄存器,也缺乏访问检查;在装载段寄存器之前,相应段必须已驻留在物理内存中。随后,Intel 在后续产品中逐步引入虚拟存储与保护机制,并扩展地址能力。这套精心设计、细节繁多的体系被称为 IA-32,其目标是在尽量避免安全漏洞的前提下实现共享与隔离。

IA-32 的第一项改进,是将传统的"用户态 / 内核态"两级模型扩展为四级保护环:0 级权限最高(内核),3 级最低(用户)。每一级拥有独立栈,以防止跨级攻击。同时引入了类似分页表的数据结构,用于记录段的物理位置及其访问约束。

在此基础上,IA-32 允许操作系统与用户共享整个地址空间,并支持在严格保护下从用户代码安全地调用内核例程。调用过程中,系统栈与用户栈分离,参数在保持原有权限级别的前提下传递,从而避免"借内核之手"访问用户本无权限的数据,这类漏洞正是典型的"特洛伊木马"问题。

1)有界检查与映射

IA-32 的分段寄存器不再直接保存基址,而是保存指向"描述符表"的索引。段描述符相当于分页系统中的页表项,包含:

  • 存在位:指示段是否有效;
  • 基址:段起始物理地址;
  • 访问位:用于记录使用情况;
  • 属性:规定操作类型与保护级别;
  • 界限:限定段内合法偏移范围。

处理器将偏移量与界限比较,通过后再与基址相加形成物理地址。IA-32 还可叠加分页机制,但上述保护在不依赖分页的情况下即可成立。

2)受保护的共享

地址空间被划分为"全局"和"局部"两部分:共享段的描述符存放在全局描述符表中,进程私有段存放在局部描述符表中。程序在装载段寄存器时,给出索引并指明所用表项。硬件依据描述符属性检查权限,并确保偏移不越界。

每个段描述符包含 2 位访问级别。只有当程序以不高于该级别的权限访问段时才被允许。

例如,在薪金系统中,可以向应用提供一个只读的"薪金数据"描述符,同时提供一个可写的"当前汇总"描述符给受信任代码。应用只能读取薪金数据,并通过受控调用让受信任代码更新汇总,从而将潜在风险限制在极小范围内。

3)安全调用门

为防止任意跳入高权限代码,IA-32 引入"调用门"。调用门描述符给出一个唯一、合法的入口地址,处理器忽略调用指令中的偏移量,只允许从该入口进入受保护代码。

调用时,硬件按描述符规定的数量,将参数从调用者栈安全复制到目标级别的栈中;返回时再反向处理。这保证了参数传递的完整性与隔离性。

为避免内核以自身权限直接使用用户地址,IA-32 在段寄存器中引入"请求保护级别"。当用户参数被装载时,其请求级别随之记录。内核例程若试图以高于该请求级别的权限访问相关段,硬件将阻止该操作,从而封闭这一潜在漏洞。

通过上述机制,IA-32 在硬件层面实现了细粒度的隔离、共享与安全调用,为复杂系统提供了坚实的保护基础。

2.3 分页虚拟存储器举例

AMD 工程师在设计 64 位架构(AMD64/x86-64)时发现,IA-32 那些精巧的分段保护模型在实际中极少被使用。于是:

  • 弃繁就简:64 位模式摒弃了绝大部分分段机制,它假定所有段基址均为 0 且忽略界限检查。
  • 平面地址空间:这种模型被称为"平面(Flat)地址空间",将保护重心完全从"段"转移到了"页"。

虽然架构理论支持 64 位虚拟地址,但为了平衡硬件复杂度和成本,实际实现通常采用较少的位数:

  • 位数限制 :Opteron 实际使用 48 位虚拟地址40 位物理地址
  • 规范格式(Canonical Form):要求地址的高 16 位必须是第 47 位的符号扩展。这种设计确保了地址空间的唯一性,并为未来扩展至物理 64 位留下了空间。

1)四级层次化分页表

由于 64 位地址空间巨大,单级页表会占用惊人的空间。如果有一个64位的地址空间,实际使用 48 位虚拟地址,页面大小为4KB( 2 12 2^{12} 212),PTE大小为8字节,则一共有 2 48 / 2 12 2^{48} / 2^{12} 248/212 = 2 36 2^{36} 236= 64G个页面,也就需要64G个PTE,也就需要一个8 * 64 = 512GB的页表,相当惊人。

因此,AMD64 使用了一种多级层次结构分页表来映射地址空间,使其保持合理大小。级别数取决于虚拟地址空间的大小。下图显示了 Opteron 的 48 位虚拟地址的四级变换。

Opteron 采用的 4 级页表结构

  • 索引分解 :48 位虚拟地址被划分为 4 个 9 位 的索引字段(各对应一级页表)和 1 个 12 位的页内偏移量。

  • 为什么是 9 位? 每个页表项(PTE)占 8 字节,一个 4 KB 的标准页恰好能容纳 4096 / 8 = 512 个表项。 2 9 2^9 29 = 512,因此 9 位索引完美匹配单个页面的大小。

  • 大小 :4 级页表结构是一种树形分层的内存映射系统,理论上L4有512个PTE,对应有512个L3,512×512个L2,512×512×512个L1。但在实际运行时,操作系统仅按需 为进程实际使用 的虚拟地址区域分配页表 ,这使得绝大多数未被访问的地址空间并不占用物理内存,从而在支持巨大地址空间的同时,保持了极低的内存开销和高效的地址转换性能。

    c 复制代码
            [第4级页表] (1个)
               / \
              /   \
         [第3级页表] (最多512个)
            /  |  \
           /   |   \
      [第2级页表] (最多262,144个)
         / | \
        /  |  \
    [第1级页表] (最多134,217,728个)

CPU 进行虚拟地址到物理地址转换时,首先从 CR3 寄存器 获取第 4 级页表(PML4)的物理基地址。接着,硬件依次使用虚拟地址中的 4 个 9 位索引字段(分别对应第 4 级至第 1 级页表)进行多级查表:

  • 利用第 4 级索引在第 4 级页表中找到对应的页表项(PTE),该 PTE 中存储的是第 3 级页表的物理基址;
  • 同理,依次使用第 3、2 级索引,分别在第 3、2 级页表中查找,获取下一级页表的物理基址;
  • 最后,使用第 1 级索引在第 1 级页表中找到目标页表项,从中提取出完整的 物理页帧号(PPN)
  • 将 PPN 与虚拟地址中最低的 12 位页内偏移量 拼接,即得到最终的物理地址。

该过程由 MMU(内存管理单元) 硬件自动完成,且常辅以 TLB(转换后备缓冲区) 缓存热门映射,以加速地址转换。

2)页表项(PTE)的权限与属性控制

每个 64 位页表项(PTE)是一个精密的控制结构,其高位存储物理页帧号(PFN),低位则是一系列关键的控制位,由硬件(MMU)和操作系统协同使用,以实现内存保护和高级功能:

  • 基础权限位
    存在位 :指示该页当前是否在物理内存中。若为0,访问将触发缺页异常 ,由操作系统介入处理。
    读/写位 :决定页面是可读写(1)还是只读(0)。这是实现写时复制等机制的基础。
    用户/管理员位:划清用户态与内核态的访问边界。用户页(1)通常不允许内核直接访问,反之亦然,这是系统安全的核心基石。
  • 关键扩展位
    不执行位 :这是对抗代码注入攻击的关键安全特性 。当该位被置位时,CPU禁止将此数据页的内容当作指令执行,从而有效遏制了基于缓冲区溢出的攻击。
    页面大小位 :支持大页映射 。当在较高级别的页表项中置位时,可以直接映射如 2MB 或 1GB 的大页,从而跳过后续的页表查找层级,大幅减少TLB未命中和页表遍历的开销,提升大块连续内存(如数据库缓冲池)的访问性能。

3)硬件加速:多级TLB层次结构

四次物理内存访问的页表遍历成本极高。因此,所有现代处理器都依赖 TLB 作为页表转换的旁路缓存。Opteron(及现代x86 CPU)采用分立的、多级TLB设计,以在速度、容量和功耗间取得最佳平衡。

TLB层次结构详解:

特性 L1 TLB (指令/数据分立) L2 TLB (指令数据统一)
设计目标 速度优先,服务于最关键的访问路径。 容量与效率平衡,捕获更多的映射,减少对页表的访问。
容量 较小(例如:各 64 项),专为高频访问的"热"页设计。 较大(例如:512-1532 项),能覆盖一个典型工作集的大部分。
命中延时 极低(1-2个时钟周期),与L1缓存访问速度相当。 较高(约7-12个周期),但仍远快于访问内存中的页表。
相联度 通常为全相联,任何映射可存放在任何位置,实现最高灵活性。 多路组相联(如8路),在较大容量下保持可管理的查找速度。
映射粒度 通常支持混合页大小(4KB, 2MB, 1GB)。 主要针对4KB页,可能部分支持大页。

TLB工作流程与协同:

  1. 查找 :当CPU发出虚拟地址时,MMU首先并行查询L1指令TLB或L1数据TLB
  2. L1命中 :若在L1 TLB中找到对应的物理页帧号和权限,则在1-2个周期内完成转换,无额外内存访问。
  3. L1未命中,L2命中 :若L1未命中,则查询统一的L2 TLB。若在此命中,转换在约10个周期内完成,并将该映射预取或升级到L1 TLB中
  4. 完全未命中 :若L2 TLB也未命中,则触发一次页表遍历 ,硬件按照CR3→四级页表的顺序,自动从内存中读取各级页表项 。此过程可能消耗数百个周期。遍历成功后,不仅完成地址转换,还会将得到的映射同时填入L2和L1 TLB,以备后续使用。
  5. 上下文管理 :TLB中的映射与进程的地址空间(由CR3值标识)相关联。在进程切换时,操作系统需要刷新或标记TLB中属于旧进程的条目(例如,通过写入新的CR3值来使非全局TLB项失效),以确保进程间的隔离性。

三、ARM Cortex-A8 的存储器层次结构

Cortex-A8 是 ARM 公司推出的一款高性能、低功耗处理器核心,支持 ARMv7-A 指令集架构,广泛应用于嵌入式系统与个人移动设备(PMD),如早期的 iPhone、Android 智能手机与多媒体终端。

与 Intel、AMD 提供的"成品处理器"不同,Cortex-A8 以 IP 核心(Intellectual Property Core) 的形式授权给厂商,由后者将其与 GPU、视频编解码器、外设控制器等模块集成到同一颗 SoC(System on Chip)中,从而形成定制化处理器。

这使 Cortex-A8 成为"移动计算时代"的奠基者之一:它不仅定义了一代移动 CPU 的性能上限,也确立了 SoC 为中心的处理器设计范式。

1)IP 核心:定制化处理器的基础单元

IP 核心并不是一颗可直接焊接到主板上的芯片,而是一套可综合的硬件设计描述。厂商可以根据目标工艺、功耗预算和成本约束,对其进行裁剪和集成。

ARM 提供的核心通常有两种交付形态:

核心类型 灵活性 性能与面积 特点
硬核心(Hard Core) 较低 极高 针对特定代工工艺深度优化,以"黑盒"形式交付,只允许调整少量参数(如 L2 容量)。性能、功耗和面积最优。
软核心(Soft Core) 极高 适中 以可综合 RTL 形式交付,厂商可针对不同工艺重新综合,甚至修改局部逻辑,灵活度极高。

Cortex-A8 同时支持上述两种模式,使其既能服务于追求极致能效的移动 SoC,也能适配多样化嵌入式场景。

2)存储器层次结构:在频率与功耗之间取得平衡

Cortex-A8 的设计目标是在约 1 GHz 主频下,实现每周期最多发射 2 条指令。然而,主存访问延迟远高于一个时钟周期,因此其性能高度依赖于高效的缓存层次结构。

下图是ARM Cortex-A8的存储器层次结构,与"1.5 虚拟存储器和缓存小结"的内容相似,可参考其流程介绍。

L1 缓存 (指令/数据分离):

  • 容量:指令缓存与数据缓存各 16 KB 或 32 KB
  • 结构:四路组相联
  • 寻址方式:虚拟索引、物理标记(VIPT)
  • 访问延迟:1 个周期
  • 设计要点
    支持路径预测与流水化访问,降低分支失败后的取指惩罚;
    使用随机或近似 LRU 的替换策略,在面积与命中率之间折中;
    当 D-Cache 为 32 KB 且页大小为 4 KB 时,索引位可能跨越页边界,导致同一物理页映射到多个缓存行(Aliasing)。Cortex-A8 通过硬件检测与一致性机制避免"重影"问题,保证语义正确。

L2 缓存 (统一):

  • 容量:可选配置,128 KB ~ 1 MB
  • 结构:八路组相联
  • 寻址方式:物理索引、物理标记(PIPT)
  • 接口带宽:通过 64 位或 128 位总线连接外部存储器,支持多重并行事务
  • 作用
    在功耗可控的前提下显著降低主存访问频率;
    吸收突发访存流量,为乱序执行和预取机制提供缓冲空间。

3)地址转换:高效的 TLB 与页表管理

Cortex-A8 采用基于分页的虚拟存储体系,地址转换由 TLB(Translation Lookaside Buffer) 加速完成:

  • 结构:指令 TLB 与数据 TLB 各 32 项,全相联组织,确保高命中率
  • 支持页大小:4 KB、16 KB、64 KB、1 MB、16 MB
  • 缺失处理
    TLB 未命中时,硬件自动执行页表遍历(Page Table Walk),无需软件介入;
    采用轻量级轮询替换策略,在硬件复杂度与效果之间取得平衡。

这种"硬件托管的页表遍历"显著降低了上下文切换和缺页处理的开销,是 ARMv7 相较早期 ARM 架构的重要改进。

4)性能影响分析:缓存缺失的代价

对 Cortex-A8 的性能评估通常基于 Minnespec 基准(SPEC2000 的精简版)。需要注意:

Minnespec 的输入规模较小,其缓存缺失率显著低于完整 SPEC2000,尤其是在 L2 层级。以 1 MB L2 为例,两者缺失率差距可达 6 倍。因此,这些数据更适合用于分析"缓存行为对 CPI 的相对影响",而非用于绝对性能比较。

在该测试环境下,Cortex-A8 表现出以下特征:

  • 指令缓存缺失率: 得益于四路组相联设计,大多数计算密集型程序的 I-Cache 缺失率低于 1%。
  • 访存惩罚(Penalty)
    L1 缺失:约 11 个周期
    L2 缺失:约 60 个周期(访问外部 DDR SDRAM)

这意味着:

  • L1 的设计直接决定了处理器能否接近"每周期两条指令"的理想吞吐;
  • L2 的容量与命中率则决定了系统在真实负载下的"长尾性能";
  • 一次 L2 缺失相当于几十条指令的执行时间,其代价足以淹没流水线带来的并行优势。

因此,Cortex-A8 的整体架构体现出一种典型的移动处理器设计哲学:

以极低延迟的 L1 缓存支撑高 IPC,以容量适中的 L2 缓存控制能耗,再辅以硬件化的地址转换机制,将"高性能"与"低功耗"维持在可接受的平衡点上。