首页 / 知识库 / 进大厂必备计算机基础 / 计算机操作系统原理

08 - 每个程序都以为自己独占内存

这是操作系统里最精妙的设计之一,也是面试常问的虚拟内存。理解它,很多问题会豁然开朗。

先看一个「不可能」的现象

每个程序在编写和运行时,都觉得自己独占了一整块从 0 开始的、连续的、超大的内存。可实际上:

  • 物理内存就那么点,还被几十个程序分着用;
  • 两个程序里都可能用到「地址 0 号」这个位置,它们怎么没打架?

秘密是:程序看到的地址,全是「假的」。

虚拟地址 vs 物理地址

  • 虚拟地址:程序自以为在用的地址。每个程序都有一套自己的、从 0 开始的完整虚拟地址空间,互不干扰。
  • 物理地址:内存条上真实的位置。

程序每次访问内存,用的都是虚拟地址。OS 和 CPU 里一个叫 MMU(内存管理单元) 的硬件,会在背后偷偷把虚拟地址翻译成真实的物理地址

程序说:我要访问「虚拟地址 100」

              ▼   (MMU 查一张对照表,做翻译)
实际访问:物理内存的「真实地址 8532」

程序全程蒙在鼓里,它以为自己独占内存,其实 OS 在背后把它安排到了物理内存的犄角旮旯,还可能被分散到好几处。

就像酒店:每个客人拿到的都是「房卡」(虚拟地址),上面写着「豪华套房」。至于这间套房物理上在几楼几号,酒店(OS)说了算,客人不用也没法知道。不同客人的「豪华套房」互不冲突。

分页:把内存切成小方块

翻译不是一个地址一个地址硬记的,那样表太大了。OS 的办法是分页

  • 把虚拟地址空间切成大小相等的小块,叫页(Page)(通常 4KB)。
  • 把物理内存也切成同样大小的小块,叫页框(Frame)
  • 维护一张页表(Page Table),记录「每一页对应哪个物理页框」。

翻译地址时,MMU 查页表就知道某一页现在落在物理内存的哪一块。这样管理和翻译都变得高效、灵活。

虚拟内存一口气解决了多少问题

理解了这套机制,回头看会发现它顺手解决了一堆难题:

1. 进程隔离(安全) 每个进程有自己独立的页表。进程 A 的虚拟地址只会被翻译到分给 A 的物理页框,根本没法访问到 B 的内存——第 02 章说的「保安」和第 03 章的「进程隔离」,底层就是靠这个实现的。

2. 内存可以「超卖」 某一页暂时用不到?把它挪到硬盘(就是上一章的 swap),页表里标记一下「这页现在在硬盘上」。程序要用时再换回来。于是程序能用的内存看起来比物理内存大得多。

3. 内存不用连续 程序以为自己的内存是连续的一整块,物理上却可以东一块西一块散落在内存各处,OS 通过页表把它们「拼」成程序眼中连续的样子。内存利用率大大提高。

4. 方便共享 多个进程要用同一份东西(比如同一个系统库),可以让它们的页表都指向物理内存里的同一块,省内存。

缺页:数据不在内存时

程序访问某一页,如果 MMU 一查页表发现「这页现在在硬盘上,不在内存里」,就会触发一个缺页中断(Page Fault):OS 暂停程序,赶紧把那一页从硬盘换回内存,更新页表,再让程序继续。程序对这一切毫无感知,只是那一下会稍微慢一点。

小结

  • 虚拟内存让每个程序都以为自己独占一整块连续的大内存,用的都是虚拟地址
  • CPU 里的 MMU 借助页表,把虚拟地址实时翻译成真实的物理地址,程序全程不知情。
  • 实现方式是分页:内存切成固定大小的页 / 页框,用页表记录对应关系。
  • 它一举解决了:进程隔离(安全)、内存超卖(配合 swap)、无需连续(提高利用率)、方便共享
  • 访问到不在内存的页会触发缺页中断,OS 负责从硬盘换回来。

下一章 → 09 - 文件系统:数据怎么存在硬盘上 | 回到 README 目录