「翻译」虚拟内存介绍

原文地址An introduction to virtual memory
计算机是用来执行简单任务的复杂机器:比如 上网、文本编辑、网页服务、视频游戏…… , 还可以对数据进行操作 , 图片 音乐 文本 数据库……
当计算机不使用的时候 , 程序和数据都安静地躺在磁盘里 , 即便你关机了数据也会在 。 运行一个应用就是让处理器(CPU)读取和执行程序代码的机器指令处理数据 。
磁盘可以保存大量的信息 , 但存取的时候都非常非常慢 , 比CPU慢得多 , 如果CPU直接从磁盘中读取指令 , 显然会成为整个系统的性能瓶颈 。 为此 , 主存/内存(RAM)就诞生了 , 内存是比磁盘小 , 但读写速度快得多的存储设备 。 应用运行时其程序和数据首先拷贝到内存中 , 这样处理器就可以在内存中读写数据 , 从而避免了大量的等待 。
主存可以看作是一个很长的单元格列表 , 每个单元格包含一些二进制数据 , 并用一个称为内存地址的数字进行标记 。 根据系统中可用的主存数量 , 内存地址的范围从0到N 。 程序使用的地址范围称为地址空间 。
「翻译」虚拟内存介绍文章插图
早期计算机的内存使用【「翻译」虚拟内存介绍】在早期的计算机中(现在也见于某些嵌入式系统) , 程序是可以访问整个内存空间的 , 内存的管理也得由程序员自己实现 。 在这种类型的计算机上写程序是一种挑战 , 因为程序员得找到一种好的内存管理方式 , 以确保各程序之间内存不会覆盖和干扰 。 问题在多任务的时候更复杂 , 因为程序员必须面对更严峻的问题?

  1. 内存布局 —— 当第一个程序分配走特定数目的内存空间后 , 可用内存初始范围将不再是0-n了 , 开发者得妥善处理内存偏移 。
  2. 内存分段 。 当内存被不断地分配回收之后 , 可用空间会逐渐变成越来越小的碎片 , 会越来越难以找到整块的空间分配给新的程序或者数据 。
  3. 安全性 。如果程序A不小心覆盖了程序B的数据?或者有人故意从其他进程中读取敏感数据 , 比如密码和信用卡信息?
所以在1960年代初期 , 找到一种能自动管理内存方式尤为关键 , 这可以大幅度简化代码编写 , 并修复潜在的内存问题 。 最终诞生了我们今天要说的虚拟内存 。
虚拟内存简介在虚拟内存中程序并不直接访问物理内存 , 而是和虚拟内存地址空间交互 。 操作系统和处理器将虚拟内存地址转化为物理内存地址 。
进程每次的内存的读写都是在虚拟内存地址之上的 , 虚拟地址并不执行特定的物理地址 , 所以每次内存访问时程序并不知道硬件层面发生了什么 。
「翻译」虚拟内存介绍文章插图
虚拟内存的优点在上图中我们可以看到虚拟地址和物理地址之间的映射关系 , 这种映射关系带来了两个好处 。
  1. 每个程序都可以有一个从0开始的虚拟内存地址空间 , 这大大简化了程序猿的编码 , 因为不需要再去手动维护内存地址的偏移了 。
  2. 即便物理内存地址不连续但虚拟内存地址可以做到总是连续的 , 这样操作系统算是间接完成了将内存碎片合并成一块可用内存的艰巨工作 。
虚拟内存机制也解决了内存有限的问题 , 因为操作系统可以给每个进程分配比实际内存大的多的虚拟内存空间 。 另外虚拟内存也可以保证安全性 , 程序A无法在不触发操作系统错误的情况下读取到程序B的数据 , 下文中我们将会介绍这一切是如何实现的 。
分页虚拟内存机制需要一个地方来存储虚拟地址和物理地址之间的映射关系 , 因为我们需要将虚拟地址X转化为物理地址Y , 当然你不能用1:1的映射 , 因为这样的映射关系数据将和实际内存一样大 。
现代虚拟内存将多个固定大小的整块物理内存合并成一个列表管理 , 解决了上述映射关系过大的问题 , 这种实现方式就叫做分页 。 其中每一块在虚拟内存中叫页面在物理内存中叫页框 , 每一个页面和页框是对应的 。 CPU的内存管理单元(MMU)以一种叫做页表的特殊数据结果存储这虚拟页框到物理页框的映射关系 。 页表好比是有个数据库 , 每一行都存储这页号+页框对应的物理内存地址 。 每个进程在MMU中都会有自己的页表 , 如下图 。