操作系统

虚拟内存:程序看到的地址是怎么翻译的

每个程序都以为自己独占一大片内存,其实背后有一层翻译。

概况

以 Linux 上一个程序读取自己内存中的一个变量为例,只讲概念。

一句话总结
程序用虚拟地址,硬件按页表翻译成物理地址
好处
隔离、布局统一、物理内存可以不连续
页
Linux 在 x86-64 上通常以 4KB 为一页;大页(如 2MB)用来减少翻译开销
页表
每个进程一套,由内核维护
TLB
最近翻译结果的缓存
权限位
每页记着可读、可写、可执行、用户可访问,违反即触发异常
多级页表
x86-64 通常 4 级,只为用到的区域建表
写时复制
fork 后共享只读页,写时才复制
swap
不常用的页换到磁盘;频繁换页叫抖动

步骤 9 / 9

内存不够时:换出到磁盘

物理内存不够时,内核可以把不常用的页写到磁盘,腾出空间。

虚拟内存让“程序以为有的内存”可以大于物理内存。 内核挑最近不常用的页写到交换区(swap)或丢掉可重新读取的文件页,并在页表里标记“不在内存”;再访问时触发缺页,再读回来。 读磁盘比读内存慢成千上万倍,所以频繁换进换出(抖动)时整台机器会变得非常卡。

程序
地址翻译
物理内存
程序
地址翻译(MMU)
物理内存
冷页写入 swap
事件日志(9)
当前状态发生了什么
用户态程序想读取一个变量
用户态地址落在“堆”区域
用户态查页表,得到物理地址
翻译中逐级查找页表
动手实验地址被拆成 4 个 9 位下标和 12 位偏移
用户态TLB 命中,翻译几乎零开销
共享多个页表指向同一页
用户态程序读到了变量的值
换出腾出物理页