--- date: 2021-03-21 --- - [前言](#前言) - [操作系统的工作流程](#操作系统的工作流程) - [虚拟内存是什么?](#虚拟内存是什么) - [虚拟页(Virtual Page,PP)](#虚拟页virtual-pagepp) - [内存管理](#内存管理) - [GitHub 项目](#github-项目) - [参考资料](#参考资料) # 前言 昨天跟前架构师讨论了下技术问题,突然谈到了虚拟内存,理解不到位,再研究记录一下。 # 操作系统的工作流程 操作系统中的 `CPU` 和`主内存`(Main memory)都是稀缺资源,所有运行在当前操作系统的进程会共享系统中的 CPU 和内存资源,操作系统会使用 `CPU 调度器`分配 CPU 时间并引入`虚拟内存`系统以`管理物理内存`,本文会分析操作系统为什么需要虚拟内存。 ![](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321113537.png) # 虚拟内存是什么? 虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如 RAM)的使用也更有效率。 操作系统物理内存和进程之间的中间层,为进程隐藏了物理内存,提供更加简洁和易用的接口、更复杂的功能。 ![](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321110852.png?x-oss-process=style/yano) 进程持有的虚拟地址(Virtual Address)会经过内存管理单元(Memory Mangament Unit)的转换变成物理地址,然后再通过物理地址访问内存: ![虚拟内存系统](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321111119.png?x-oss-process=style/yano) 主内存比较稀缺,顺序读取只比磁盘快 1 个数量级,但是随机读取数据的速度是磁盘的 100000 倍!充分利用内存的随机访问速度是改善程序执行效率的有效方式。 操作系统以`页`为单位管理内存,当进程发现需要访问的数据不在内存时,操作系统可能会将数据以页的方式加载到内存中,这个过程是由上图中的`内存管理单元(MMU)`完成的。操作系统的虚拟内存作为一个`抽象层`,起到了以下三个非常关键的作用: - 虚拟内存可以利用内存起到缓存的作用,提高进程访问磁盘的速度; - 虚拟内存可以为进程提供`独立的内存空间`,简化程序的链接、加载过程并通过动态库共享内存; - 虚拟内存可以控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性; # 虚拟页(Virtual Page,PP) 虚拟内存可以看成是在磁盘上的一片空间,当这片空间某个部分访问比较频繁时,这部分数据会以页为单位缓存到主存,以加速 CPU 访问数据的性能。虚拟内存利用空间较大的磁盘存储作为「内存」并使用主存储缓存进行加速。在操作系统看起来,虚拟内存很大、很快。 ![](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321112259.png?x-oss-process=style/yano) 虚拟内存中的虚拟页(Virtual Page,PP)可能处于以下的三种状态: - 未分配(Unallocated):没有被进程使用的,空闲的虚拟内存,不占用虚拟内存磁盘的任何空间 - 未缓存(Uncached):仅加载到磁盘的内存页 - 已缓存(Cached):已经加载到主存的内存页。 当用户程序访问未被缓存的虚拟页时,硬件就会触发缺页中断(Page Fault,PF),操作系统需要将磁盘上未被缓存的虚拟页加载到物理内存中。 ![虚拟内存的缺页中断](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321112515.png?x-oss-process=style/yano) 因为主内存的空间是有限的,当主内存中不包含可以使用的空间时,操作系统会从选择合适的物理内存页`驱逐回磁盘`,为新的内存页让出位置,选择待驱逐页的过程在操作系统中叫做`页面替换(Page Replacement)`。缺页中断和页面替换技术都是操作系统`调页算法(Paging)`的一部分,该算法的目的就是充分利用内存资源作为磁盘的缓存以提高程序的运行效率。 # 内存管理 虚拟内存可以为正在运行的进程提供独立的内存空间,制造一种每个进程的内存都是独立的假象,在 64 位的操作系统上,每个进程都会拥有 256 TiB 的内存空间,内核空间和用户空间分别占 128 TiB。因为每个进程的虚拟内存空间是完全独立的,所以它们都可以完整的使用 0x0000000000000000 到 0x00007FFFFFFFFFFF 的`全部内存`。 ![操作系统的虚拟内存空间](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321112802.png?x-oss-process=style/yano) 虚拟内存空间只是操作系统中的逻辑结构,就像我们上面说的,应用程序最终还是需要访问物理内存或者磁盘上的内容。因为操作系统加了一个虚拟内存的中间层,所以我们也需要为进程实现`地址翻译器`,实现从虚拟地址到物理地址的转换,页表是虚拟内存系统中的重要数据结构,每一个进程的页表中都存储了`从虚拟内存到物理内存页的映射关系`,为了存储 64 位操作系统中 128 TiB 虚拟内存的映射数据,Linux 在 2.6.10 中引入了四层的页表辅助虚拟地址的转换。 因为有多层的页表结构可以用来转换虚拟地址,所以多个进程可以通过虚拟内存`共享物理内存`。能共享一些常见的动态库减少物理内存的占用,所有的进程都可能调用相同的操作系统内核代码,而 C 语言程序也会调用相同的标准库。 除了能够共享内存之外,独立的虚拟内存空间也会简化内存的分配过程,当用户程序向操作系统申请堆内存时,操作系统可以分配几个连续的虚拟页,但是这些虚拟页可以对应到物理内存中不连续的页中。 ![](http://yano.oss-cn-beijing.aliyuncs.com/blog/20210321113116.png?x-oss-process=style/yano) # GitHub 项目 GitHub 项目 [Thinking_in_Java_MindMapping](https://github.com/LjyYano/Thinking_in_Java_MindMapping),记录 Coding 的思考,欢迎关注! # 参考资料 - [为什么 Linux 需要虚拟内存](https://draveness.me/whys-the-design-os-virtual-memory/) - [虚拟内存-维基百科](https://zh.wikipedia.org/wiki/%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98)