前言:存为了更加高效并且少出错,现代操作系统提供了一种对主存的抽象概念,叫做虚拟内存。虚拟内存是硬件异常,硬件地址翻译,主存,磁盘文件和内核软件的完美交互,他为每个进程提供了一个大的,一致和私有的地址空间,虚拟内存提供三个能力:它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在主存和磁盘之间来回传送数据,通过这种方式高效使用了主存他为每个进程提供了一致的地址空间,从而简化了内存管理它保护了每个进程的地址空间不被其他进程破坏。
物理和虚拟寻址
计算机主存被组织成一个由M个连续的字节大小的单元组成的数组。每字节都有一个唯一的物理地址,每一个字节的地址为0,接下来的字节地址为1,在下一个为2,以此类推,给定这种简单的结构,CPU访问内存的最自然的方式就是使用物理地址,我们把这种方式称为物理寻址。
现在操作系统都是用虚拟寻址的寻址形式。CPU通过生成一个虚拟地址来访问内存,这个虚拟地址在送到内存之前会被转换成适当的物理地址,将虚拟地址转换成物理地址这一过程成为地址翻译。需要硬件系统个操作系统之间的紧密配合,CPU芯片上的叫做内存管理单元的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。
地址空间:地址空间是一个由非负整数组成的有序集合。一个地址空间大小是由表示最大地址所需要的位数描述的,例如一个包含N=2^n个地址的虚拟地址空间就叫做一个n位地址空间。现在操作系统通常支持32位或者64位寻地址空间。
地址空间区分了数据对象和他们的属性,一旦认识到这种区别,我们就可以将其推广,允许每个数据对象有多个独立的地址空间,其中每个地址都选自不同的地址空间,这是虚拟内存的基本思想。主存中的每个字节都以一个选自虚拟地址空间和一个选自物理地址空间的物理地址。
虚拟内存作为缓存的工具:
虚拟内存被组织成一个有存放在磁盘上的N个连续的字节大小的单元组成的数组,每字节都有唯一的虚拟地址,作为数组的索引,磁盘上的内容被缓存在主存中,和存储器层次结构中的其他缓存一样,磁盘上的数据被分割成块,这些块被分割成磁盘和主存之间的传输单元,VM系统通过将虚拟内存分割成称为虚拟页的大小固定的块来处理这个问题,每个虚拟页的大小为P=2^p字节,类似的,物理内存被分割成物理页,大小为p字节。被称为页帧。
在任意时刻,虚拟页面都分为三个不相交的子集:
- 未分配的:VM系统还未分配的页,未分配的块没有任何数据和他们相关联,因此,也就不占用任何磁盘空间。
- 缓存的:当前已经缓存在物理内存中的已经分配的页
- 为缓存的:未缓存到物理内存中已分配的页
DRAM缓存的组织结构:
SRAM为CPU和主存之间的L1,L2,L3高速缓存DRAM表示虚拟内存系统的缓存,他在主存中缓存虚拟页DRAM要比SRAM慢10倍,而磁盘要比DRAM慢大约100000倍,因此DRAM不命中比起SRAM不命中要昂贵的多。因为DRAM缓存不命中是由磁盘来服务的。
在习惯说法中,DRAM缓存不命中,我们称为缺页。
一次缺页的处理过程:
如上图所示:CPU引用了vp3一个字,vp3并未缓存在内存中(DRAM)中,地址翻译硬件会从内存中读取PTE3,从有效位推断vp3未被缓存,并触发一个缺页异常,缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是vp4,如果vp4被修改了,那么内核就会将其复制回磁盘,无论哪种情况,内核都会修改vp4的页表条目PTE4,反应vp4不再缓存在主存中这一事实。
接下来,内核从磁盘复制vp3到内存中的原来vp4的位置,更新PTE3,随后返回,当异常处理程序返回时,他会重新启动导致缺页的指令,该指令会将导致缺页的虚拟地址发送给地址翻译硬件,现在vp3已经被缓存到主存中了,那么页命中也能由地址翻译硬件处理了。
下面是完成却也异常处理的示意图:
在虚拟内存的说法中,块被称为页,在磁盘和内存之间传送页的活动叫做页面调度。页从磁盘换入DRAM和从DRAM换出磁盘,一直到最后时刻,也就是当不命中发生时,才换入页面的这种策略,称为按需页面调度。调用malloc会使磁盘新的页面在磁盘上产生。
局部性
尽管在整个运行期间程序引用的不同页面的总数可能超出物理内存总大小,但是局部性原则保证了在任意时刻,程序趋向在一个较小活动页面上工作,这个集合叫做工作集,或者常驻集合,在初始开销,将工作集页面调度到主存之后,接下来对这个工作集的引用将导致命中,而不会产生额外开销。只要程序有好的时间局部性,虚拟内存系统就能工作相当好,如果工作集的大小超出了物理内存的大小,程序将产生一种不幸的状态,叫做抖动,这个时候页面会被不停的换进与换出。使得程序性能下降。
按需页面调度和独立的虚拟地址空间的结合,对系统中内存的使用和管理造成了深远的影响,VM简化了连接与加载,代码和数据共享,以及应用程序的内存分配。
简化链接:独立的地址空间允许每个进程的内存映像使用相同的基本格式,而不管代码和数据实际存放在物理内存何处。
简化加载:虚拟内存还使得容易向内存中加载可执行文件和共享对象文件。
将一组连续的虚拟页映射到任意一个文件中的任意位置表示法称为内存映射。
简化共享:独立的地址空间为操作系统提供了一个管理用户进程和操作系统的自身之间共享的一致机制。一般而言,每个进程都有自己私有的代码、数据、堆以及栈区,是不和其他进程共享的,在这种情况下,操作系统创建页表,将相应的虚拟页映射到不连续的物理页面。
在一些情况下,还是需要进程来共享数据和代码,比如每个进程必须调用相同的操作系统内核代码,而每个C程序都会调用C标准库中的程序,操作系统通过将不同进程中适当的页面映射到相同的物理页面,从而安排多个进程共享这部分代码的副本,而不是每个进程单独拥有内核或者C标准库。
简化内存分配:虚拟内存为用户提供了简化内存分配的机制,当一个运行在用户进程中的程序要求分配额外的堆空间的话,操作系统分配相应适当的数字个连续个虚拟内存页面,并且将它们映射到物理内存中任意位置的k个任意的物理页面,由于页表的工作方式,没必要分配连续的物理页面,而是随机分散在物理内存中。
虚拟内存作为内存保护的工具:
现代计算机系统必须为操作系统提供手段来控制对内存系统的访问,不应该允许一个进程修改他的只读代码段,而且也不用改允许他读或者修改任何内核中的代码或者数据结构。不应该允许他读或者写其他进程的私有内存,并且不允许他修改与其他进程共享的虚拟页面,除非共享者都显式地允许他这么做。
每次CPU生成一个地址时,地址翻译硬件都会读一个PTE,所以通过在PTE上添加一些额外的许可位来控制对一个虚拟页面的访问十分简单。
更多LINUX内核免费文档 面试资料教程视频资料关注 私信1自行领取