1. 简介

在用户程序需要分配内存时,它通常会通过系统调用请求操作系统完成。这种方式可以让操作系统合理地在各个运行中的进程之间管理内存资源。

除此之外,操作系统还实现了虚拟内存(Virtual Memory)机制,其核心目标是通过将部分内存数据临时存放到硬盘中,从而扩展内存的可用容量。

借助这一机制,某个内存地址可能并不在物理内存中。解决这个问题的方法就是使用虚拟地址(Virtual Address)代替物理地址(Physical Address)。

2. 虚拟内存机制

虚拟内存的核心概念之一是分页(Paging)。它将内存划分为固定大小的块,通常为 4KB,这些块被称为“页(Page)”:

Physical memory pages

如果没有这种分页机制,我们就必须为每一个虚拟地址分配对应的物理内存空间。而通过分页的方式,我们可以按页为单位进行虚拟地址到物理地址的映射,从而节省大量资源。

这些页的地址信息,包括它们是否在物理内存中,还是被交换到硬盘中,都保存在一个称为页表(Page Table)的结构中。

每个页表项(Page Table Entry,简称 PTE)都包含该页在物理内存中的位置信息:

Page Table

页表中还包含一些安全信息,比如该页属于哪个进程,防止一个进程访问或修改另一个进程的内存。不过为了本文的简洁性,我们主要关注物理页的映射信息。

负责地址转换的硬件是内存管理单元(MMU),它集成在 CPU 中。而操作系统则负责将页面换入换出硬盘。接下来我们看看它们在地址转换过程中的协作机制。

3. 地址转换流程

假设我们有一段简单的 C 语言代码:

a = 42;

当操作系统启动这个程序后,CPU 会执行该指令。在执行过程中,CPU 需要知道变量 a 的物理地址。于是它会请求 MMU 进行地址转换。

MMU 会从虚拟地址中提取出页号,并查询转换后备缓冲区(TLB)。TLB 是 MMU 内部缓存的页表部分,用于提高地址转换效率。

✅ 如果 TLB 中能找到对应的页号,则 MMU 直接返回对应的物理页号;
❌ 如果找不到,则 MMU 需要去主内存中的页表查找对应的页表项。

如果该页当前不在内存中(即被换出到硬盘),MMU 会触发一个缺页异常(Page Fault),由操作系统处理。操作系统会使用某种页面置换算法(如 FIFO、LRU)选择一个不常用的页换出到硬盘,然后将所需的页加载回内存。

一旦 MMU 成功获取到物理地址,就可以完成 CPU 的访问请求。

4. 地址转换详解

我们继续以上面的例子说明,假设变量 a 的虚拟地址是 0x00018004,在 32 位系统中。

虚拟地址由两部分组成:

  • 页号(Page Number):用于查找页表项;
  • 偏移量(Offset):用于定位页内的具体地址。

在 4KB 分页机制下,页内偏移量需要 12 位(因为 2^12 = 4096)。因此,虚拟地址的低 12 位表示偏移量,剩下的 20 位表示页号。

这意味着在 32 位系统中,最多可以有 2^20 = 1,048,576 个页。我们的虚拟地址结构如下图所示:

virtual address sample

假设 MMU 提取到的页号是 0x00018,并在 TLB 或页表中查得其对应的物理页号是 0x0020。然后将偏移量 0x004 拼接到物理页号后面,最终得到物理地址 0x0020004

如下图所示,整个地址转换过程清晰明了:

Translate Virtual to Physical Address

5. 总结

虚拟内存是现代操作系统中非常关键的一项机制,它使得系统可以突破物理内存的限制,通过将不常用的内存页换出到硬盘来实现内存扩展。

地址转换机制依赖于页表结构,记录虚拟页与物理页之间的映射关系,并结合页内偏移量完成最终的地址定位。

需要注意的是,不同的系统可能采用不同类型的页表结构,有些系统采用多级页表目录(Page Directory)来管理页表,此时地址转换还需要额外的目录解析步骤。

✅ 总结要点:

  • 虚拟内存通过分页机制实现;
  • 页表记录虚拟页与物理页的映射;
  • MMU 负责地址转换;
  • TLB 用于加速地址转换;
  • 缺页异常由操作系统处理;
  • 多级页表结构可提升大内存管理效率。

原始标题:Virtual Memory Address