虚拟内存是什么:进程地址空间、页表与 TLB
围绕进程虚拟地址空间、页表切换和 TLB ,解释操作系统如何为每个进程提供独立的内存视图,以及地址转换为什么会影响切换开销。
相关工具
程序看到的内存不等于物理内存
一个程序运行时会访问代码、数据、堆和栈,但它使用的地址并不一定是内存条上的真实位置。程序看到的是一套由操作系统和处理器共同维护的虚拟地址空间。对程序来说,自己的代码和数据像是分布在一片连续、稳定的空间里;至于这些地址最终对应哪一块物理内存,由系统负责转换。
这种抽象让程序不必直接处理物理内存的分配细节,也让不同进程可以使用相似的地址而不必互相覆盖。浏览器进程和编辑器进程都可以拥有从低地址开始的代码和数据区域,系统通过不同的地址映射把它们隔离开。进程切换时,切换的不只是 CPU 寄存器,还包括与地址空间相关的管理信息。
程序使用虚拟地址,处理器和页表把它转换为物理内存中的实际位置。
每个进程都有自己的地址空间
进程拥有独立的虚拟地址空间,是操作系统实现隔离的重要方式。进程 A 访问地址 0x1000,并不意味着进程 B 访问同一个数值地址时就会看到 A 的数据。两个地址空间可以分别把这个虚拟地址映射到不同的物理页面,甚至让其中一个地址暂时没有可用映射。
地址空间的独立性也解释了为什么一个普通进程不能随意读取另一个进程的内存。访问动作会经过处理器的地址转换和权限检查,只有映射存在且权限允许时,访问才会继续。如果程序访问了没有映射的地址,系统会产生异常,由操作系统决定是建立映射、终止程序,还是报告访问错误。
页表记录地址如何映射
为了完成虚拟地址到物理地址的转换,系统需要保存映射关系。页表就是这样的记录结构。虚拟地址通常被拆成页号和页内偏移,页号用于查找页表项,页内偏移则表示页面中的具体位置。查找到页表项后,系统得到物理页号,再和原来的页内偏移组合成物理地址。
页表项除了记录物理页号,还可能包含存在位、读写权限、用户或内核权限等信息。这样,页表不仅负责“指向哪里”,还参与判断“能不能访问”。一个进程拥有自己的地址空间,也就意味着它需要一套与其他进程区分开的映射记录。
虚拟地址拆成页号和页内偏移,页号查找页表得到物理页号,再组合出物理地址。
为什么不能每次都慢慢查页表
如果每次访问内存都要先访问页表,再访问真正的数据,就会增加额外的内存访问。程序本来只想读取一次数据,地址转换却可能让处理器先完成一次或多次页表查找。对频繁访问的程序来说,这种额外步骤会明显影响执行速度。
处理器因此会使用 TLB,也就是地址转换旁路缓存,保存最近使用过的虚拟页到物理页映射。下一次访问同一页面时,如果在 TLB 中命中,就可以少做一些页表查找;如果没有命中,系统仍然需要查页表并把新的映射放入 TLB。TLB 的价值来自局部性:程序在一段时间里往往会反复访问相邻或近期访问过的地址。
TLB 命中时直接得到物理页号,未命中时再查页表并更新缓存。
进程切换为什么会影响地址转换效率
每个进程都有自己的虚拟地址空间和页表。进程从 A 切换到 B 时,系统需要让处理器使用 B 的地址映射。页表发生变化后,原来缓存的部分地址映射可能不再适用于新的进程,TLB 也可能出现失效或命中率下降。接下来一段时间,处理器需要重新查找并填充地址转换缓存。
这也是进程切换成本的一部分。进程切换不仅保存和恢复寄存器,还可能改变地址空间,带来页表相关的工作和 TLB 缓存影响。同一进程内的线程通常共享进程地址空间,线程切换主要保存自己的寄存器和栈等运行现场,因此在这一点上不会像跨进程切换一样改变整套地址映射。
权限检查让地址空间不仅是位置表
页表还承担权限控制职责。代码区域通常不应被随意写入,某些数据区域需要可写但不可执行,内核空间也不应该被普通用户程序直接访问。处理器在完成地址转换时,会同时检查页表项中的权限信息。地址存在并不代表所有操作都被允许。
这种检查把内存隔离落实到了每次访问上。程序访问自己的可读区域通常可以继续,访问未映射区域或越权写入时,处理器会产生异常并转交操作系统处理。操作系统可以据此终止违规进程,也可以在合法场景下补充映射或完成其他管理动作。
虚拟内存带来的实际好处
虚拟内存首先提供了进程之间的隔离边界。程序可以用相对稳定的地址组织代码和数据,系统则负责把不同进程的地址映射到合适的物理位置。它还允许物理内存的分配更加灵活:虚拟地址空间可以比当前实际占用的物理页面更大,只有真正访问到的区域才需要逐步建立对应关系。
虚拟内存也让共享变得可控。两个进程如果确实需要共享一块数据,可以让相关虚拟页面映射到同一片物理内存,并配置合适的访问权限;如果不需要共享,则保持不同映射。隔离和共享并不矛盾,关键在于映射关系是否由系统明确建立。
遇到内存问题时应该怎样观察
看到程序占用内存增加,不要马上把所有现象都归因于“物理内存不够”。需要区分进程的虚拟地址空间、实际驻留的物理页面、共享页面和文件映射。一个进程的虚拟空间很大,并不代表它已经占用了同样大小的物理内存;多个进程显示的共享区域,也不一定需要重复计算。
如果程序在进程切换后变慢,还要考虑地址转换缓存是否频繁失效,工作集是否变化,以及系统是否在等待页面或其他资源。沿着“虚拟地址如何转换、页面是否存在、权限是否允许、缓存是否命中”这条路径观察,比只看一个总内存数字更接近问题本身。
用一条主线记住虚拟内存
虚拟内存为每个进程提供独立的地址空间,页表记录虚拟页面与物理页面的映射,TLB 缓存近期使用过的映射以加快转换。进程切换时,系统可能需要切换地址空间并承受地址转换缓存变化;线程在同一进程内切换,通常共享这套地址空间。
理解虚拟内存时,可以始终追问四件事:程序使用的是什么地址,页表把它指向哪里,访问权限是否允许,转换结果是否能在 TLB 中命中。这样,进程隔离、地址转换、切换成本和内存访问速度就会落到同一套逻辑上。
常见问题
虚拟地址是不是假的地址?
它不是无意义的地址,而是程序使用的地址表示。操作系统和处理器会通过页表把它转换为物理地址,并在转换过程中完成权限检查和隔离。
TLB 的作用是什么?
TLB 缓存近期使用过的虚拟页到物理页映射。命中时可以减少页表查找,未命中时仍需要查页表并更新缓存。
为什么进程切换可能比线程切换更重?
进程切换可能改变虚拟地址空间和页表,进而影响 TLB 命中;同一进程内的线程通常共享地址空间,主要切换各自的寄存器、栈和执行现场。