「翻译」虚拟内存介绍( 二 )


「翻译」虚拟内存介绍文章插图
页表到页框的转化虚拟内存地址由两部分组成

  1. 页号(页索引) , 标识这个虚拟内存地址属于哪个页面 。
  2. 页内偏移 , 标识这个地址在页框中的具体位置 。 这些信息足够MMU将一个虚拟地址转化为物理地址了 。 当一个进程读写一个虚拟地址时 , 它先唤醒MMU从虚拟地址中截取出页号并根据页表找到相应的页框 , 当页框根据页内偏移计算出实际的物理地址 , 到这里转化就完成了 。 这时候程序就有了一个实际可读写的物理内存地址 。
虚拟内存的背后当程序有了连续、整洁的虚拟内存空间后 , 操作系统和硬件在后台对物理内存做一些很疯狂的事了 。 例如:操作系统的延时加载 , 数据并不是在程序开始运行前就加载数据 , 而是等到程序实际需要使用时才加载 。 所有你会发现有些时候可能某个程序的页面对应一些不存在的页框或者是还没有分配的页框 。 比如上图中的最后两个页面就没有指向任何页框 。
像这样的取巧的手段对应用程序是完全透明的 , 它保持读取和写入自己的虚拟地址空间而不受背景噪音的影响 。 但是 , 程序迟早要访问一个没有映射到RAM的虚拟地址:该怎么办
缺页错误(中断)缺页中断发生于当程序尝试去访问一个没有映射到物理页框的虚拟地址时 。 更准确地讲 , 缺页中断发生于程序访问一个虚拟内存地址存在但在物理内存中没有对应地址的情况 。
当MMU检测到缺页中断后会将中断信息转交给操作系统 , 操作系统会尝试去找到虚拟地址到物理地址的映射 , 大多数情况下这个是一个很简单的操作 , 除非物理内存已经耗尽 。
分页 , 当物理内存不足时如何实现?分页也带来一个其他的好处 。 当物理内存不足时 , 操作系统可以把部分页面写入到磁盘中腾出空间 。 尽管不够百分百准确 , 但这种方法有时也叫做swapping(交换) , Swapping其实是把整个进程都挪到磁盘中 , 当然现在有些操作系统在必要的时候也会这么做 。
分页给了程序一种有无限可用内存的假象 。 操作系统乐观地允许一个比物理内存更大的虚拟内存地址空间 , 因为在需要的情况下数据可以被换进和换出硬盘 。 有些系统(例如Windows)会使用一个称为分页文件的特殊文件来达到这个目的 。 其他操作系统(例如Linux)有一个专用的硬盘分区 , 称为交换分区(由于历史原因 , 现代Linux执行分页而不是交换) 。
抖动当操作系统花更多的数据在执行分页而不是应用程序的时候就会发生抖动 , 一般是由一系列的缺页中断导致的 。 这种情况极易发生在当你运行大量超过物理内存大小的程序时或者硬盘交换分区没有做优化时 。 这时候操作系统会努力执行大量的缺页中断 , 持续把数据从硬盘中移动到物理内存中 , 最终可能让系统卡住 。 解决方法是加大内存或者减少进程数量或者调整交换分区大小 。
内存保护虚拟内存也提供了跨进程的安全性 。 你的浏览器无法在不侵入操作系统的情况下窥探你文本编辑器里的内容 , 因为它无法访问不属于自己的内存空间 。 内存保护机制是由MMU和其管理的页表实现的 , 也许其他硬件有不同的实现策略 。 当程序试图访问不属于它的虚拟内存时 , 会触发invalid page 错误 。 MMU和操作系统捕捉到这个信号 , 并引发一个名为段错误(segmentation fault)(Unix)或无效访问(access violation)(Windows) , 操作系统然后就会直接杀死这个进程 。
段错误和无效访问可能会程序错误而产生 。 能够手动管理内存的编程语言允许你自己管理一部分内存用来存储程序数据 , 操作系统会给你划分出一段空闲内存(又名缓冲区) , 以便根据你的程序需要进行读写 。 但是 , 没有什么可以阻止你在缓冲区边界之外读写 , 访问不属于您的程序或根本不存在的内存时 , 操作系统就会报出非法访问的信号 。