按需加载与交换空间
复习
- TLB:给频繁的地址转换加一层缓存
- 多级页表:页表本身太大时如何按需展开
- 缺页异常:访问的页面不在内存时 CPU 和内核怎样配合
TL;DR
- 按需加载:只把眼下需要的页放进内存,其余留在磁盘
- 磁盘上留出一块交换空间,专门存放暂时不用的页
- 内存紧张时,把一些页换出到交换空间,需要时再换入
- 靠这一进一出,程序才能“用着比实际内存更大的空间”
正文
上一章的缺页异常,已经暗示了一件事:页可以不在内存。这一章把这个思路讲透:内存里只放需要的页,其余的留在磁盘上。
为什么可以“偷懒”
启动一个程序时,真的需要把它的所有代码和数据都读进内存吗?
大多数情况下并不需要。程序里很多代码只在特定分支才会执行,很多数据可能整场运行都用不到。如果一开始就全部加载,既慢又占地方。
于是有了按需加载(demand loading):一开始只装入必要的一部分,其余等真正访问到时,再靠缺页异常一页一页地补进来。用到才给,不用就不给。
交换空间
可内存总有装满的时候。要腾地方,就得有地方存放被请出去、又没被删掉的页。这个“仓库”,就是磁盘上的一块交换空间(swap space)。
- 换出(swap out):把暂时不用的页,从内存写到交换空间
- 换入(swap in):等再次用到它时,从交换空间读回内存
物理内存 交换空间(磁盘)
┌────────┐ ┌────────┐
│ 常用页 │ ←换入/换出→ │ 冷门页 │
└────────┘ └────────┘
一个宏大的错觉
把“按需加载”和“交换”合在一起看,效果就出来了:
- 进程的地址空间可以比实际物理内存大得多
- 内存里只留正在用的那部分,冷的页放去交换空间
- 需要时再换回来,程序几乎察觉不到
于是程序真的会“以为”自己拥有一大片内存,而机器只有那么点物理内存——这个虚拟内存的错觉,到这里才算完整。
不过,内存满了到底该把哪一页请出去?随便挑可不行,选不好会让程序频繁缺页、越跑越慢。下一章,我们专门讨论“换出谁”的问题。
思考题
交换空间在磁盘上,磁盘比内存慢成千上万倍。既然换入换出这么慢,为什么虚拟内存还能让程序跑得动?
小结
知识点
- 按需加载:用到才把页读进内存
- 交换空间:磁盘上存放暂时不用的页
- 换出与换入
- 让程序可用空间大于物理内存
参考资料
- Wikipedia(zh):虚拟内存:按需分页与交换
- Wikipedia(zh):交换空间:swap
思考题答案(仅供参考)
因为程序访问内存有局部性:在一段时间里,它反复用到的只是少数页。这些“热”页留在内存里,绝大多数访问根本碰不到交换空间,速度不受影响;只有偶尔访问到“冷”页时,才需要从磁盘换入,慢那么一下。换句话说,交换是“兜底”,局部性让这个兜底很少被触发。相反,如果程序毫无局部性、频繁触碰大量页,就会不停换入换出,慢到无法忍受——这正是下一章“内存抖动”要讲的。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪