技术编程为什么 Linux 需要虚拟内存


操作系统中的 CPU 和主内存(Main memory)都是稀缺资源 , 所有运行在当前操作系统的进程会共享系统中的 CPU 和内存资源 , 操作系统会使用 CPU 调度器分配 CPU 时间[^1]并引入虚拟内存系统以管理物理内存 , 本文会分析操作系统为什么需要虚拟内存 。
在回答虚拟内存存在的必要性之前 , 我们需要理解操作系统中的虚拟内存是什么 , 它在操作系统中起到什么样的作用 。 正如软件工程中的其他抽象 ,虚拟内存是操作系统物理内存和进程之间的中间层 , 它为进程隐藏了物理内存这一概念 , 为进程提供了更加简洁和易用的接口以及更加复杂的功能 。
技术编程为什么 Linux 需要虚拟内存
本文插图
virtual-memory-layer
图 1 - 进程和操作系统的中间层
如果需要我们从头设计一个操作系统 , 让系统中的进程直接访问主内存中的物理地址应该是非常自然的决定 , 早期的操作系统确实也都是这么实现的 , 进程会使用目标内存的物理地址(Physical Address)直接访问内存中的内容 , 然而现代的操作系统都引入了虚拟内存 , 进程持有的虚拟地址(Virtual Address)会经过内存管理单元(Memory Mangament Unit)的转换变成物理地址[^2] , 然后再通过物理地址访问内存:
技术编程为什么 Linux 需要虚拟内存
本文插图
virtual-memory-system
图 2 - 虚拟内存系统
主存储是相对比较稀缺的资源 , 虽然顺序读取只比磁盘快 1 个数量级 , 但是它能提供极快的随机访问速度 , 从内存上随机读取数据是磁盘的 100,000 倍[^3] , 充分利用内存的随机访问速度是改善程序执行效率的有效方式 。
操作系统以页为单位管理内存 , 当进程发现需要访问的数据不在内存时 , 操作系统可能会将数据以页的方式加载到内存中 , 这个过程是由上图中的内存管理单元(MMU)完成的 。 操作系统的虚拟内存作为一个抽象层 , 起到了以下三个非常关键的作用:虚拟内存可以利用磁盘起到缓存的作用 , 提高进程访问指定内存的速度;虚拟内存可以为进程提供独立的内存空间 , 简化程序的链接、加载过程并通过动态库共享内存;虚拟内存可以控制进程对物理内存的访问 , 隔离不同进程的访问权限 , 提高系统的安全性;缓存
我们可以将虚拟内存看作是在磁盘上一片空间 , 当这片空间中的一部分访问比较频繁时 ,该部分数据会以页为单位被缓存到主存中以加速 CPU 访问数据的性能 , 虚拟内存利用空间较大的磁盘存储作为『内存』并使用主存储缓存进行加速 ,让上层认为操作系统的内存很大而且很快 , 然而区域很大的磁盘并不快 , 而很快的内存也并不大 。
技术编程为什么 Linux 需要虚拟内存
本文插图
【技术编程为什么 Linux 需要虚拟内存】virtual-memory-cache
图 3 - 虚拟内存、主存和磁盘
虚拟内存中的虚拟页(Virtual Page , PP)可能处于以下的三种状态 — 未分配(Unallocated)、未缓存(Uncached)和已缓存(Cached) , 其中未分配的内存页是没有被进程申请使用的 , 也就是空闲的虚拟内存 , 不占用虚拟内存磁盘的任何空间 , 未缓存和已缓存的内存页分别表示已经加载到主存中的内存页和仅加载到磁盘中的内存页 。 如上图所示 , 图中绿色的虚拟内存页由主存中的物理内存页(Physical Page , PP)支撑 , 所以它是已经缓存过的 , 而黄色的虚拟内存页仅在磁盘中 , 所以没有被物理内存缓存 。
当用户程序访问未被缓存的虚拟页时 , 硬件就会触发缺页中断(Page Fault , PF) , 在部分情况下 , 被访问的页面已经加载到了物理内存中 , 但是用户程序的页表(Page Table)并不存在该对应关系 , 这时我们只需要在页表中建立虚拟内存到物理内存的关系;在其他情况下 , 操作系统需要将磁盘上未被缓存的虚拟页加载到物理内存中[^4] 。