CSAPP-----虚拟内存

本节目录

1、物理和虚拟寻址

2、地址空间

3、虚拟内存作为缓存的工具

4、虚拟内存作为内存管理的工具

5、虚拟内存作为内存保护的工具

6、地址翻译

7、案例研究

8、内存映射

9、动态内存分配

10、垃圾收集

11、C程序中常见的与内存有关的错误

12、小结


     本系列文章的观点和图片均来自《深入理解计算机系统第3版》仅作为学习使用

       虚拟内存(VM)是对主存的抽象概念。虚拟内存提供了三个重要的能力:1)它将追村看成一个存储在磁盘上的地址空间的高速缓存,在主存中只保护活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式它高效的使用了主存。2)它为每个进程提供一致的地址空间,从而简化内存管理。3)他保护每个进程的地址空间不被其他进程破坏。

        虚拟内存是核心的,虚拟内存编辑计算机系统的所有层面,在硬件异常、汇编器、连接器、加载器、共享对象、文件、进程的设计中扮演重要角色。虚拟内存是强大的,虚拟内存给予应用程序强大的能力,可以创建和销毁内存片,将内存片映射到磁盘文件的某个部分,以及与其他进程共享内存。虚拟内存是危险的,每次应用程序引用一个变量、间接引用一个指针、或者调用一个诸如malloc这样的动态分配程序时,它会与虚拟内存发生交互,如果虚拟内存使用不当,应用将遇到复杂危险的与内存有关的错误。

        这一章从两个角度来看虚拟内存,前一部分描述虚拟内存是如何工作的,后一部分是描述应用程序如何使用和管理虚拟内存。

1、物理和虚拟寻址

        计算机系统的主存被组织成一个由M个连续得字节大小的单元组成的数组,每字节都有唯一的物理地址,第一个字节地址是0,接下来的是1,再下一个为2,以此类推,给定这种简单的结构,CPU访问内存的最自然的方式是使用物理地址,这种寻址方式成为物理寻址。下图为一个物理寻址的示例:

        

该示例的上下文是一条加载指令,它读取从物理地址4处开始的4字节处开始的4字节字,当CPU执行这条加载指令时,会生成一个有效的物理地址,通过内存总线把它传递给主存,主存取出从物理地址4处开始的4字节字,并将它返回给CPU,CPU会将其放在一个寄存器里。早起的PC会使用物理寻址,而且诸如数字信号处理器、嵌入式微控制器以及Cray超级计算机这样的系统仍然使用这种寻址方式,但是,现代处理器使用的是一种称为虚拟寻址的寻址形式,如下图    :

         

        使用虚拟寻址,CPU通过生成一个虚拟地址(VA)来访问主存,这个虚拟地址会在被送到内存之前转换成适当的物理地址,将一个虚拟地址转为物理地址的任务叫作地址翻译,就像异常处理一样,地址翻译需要CPU硬件和操作系统之间的紧密合作,CPU上叫作内存管理单元(MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。

2、地址空间

        地址空间是一个非负整数地址的有序集合。{0,1,2,...},如果地址空间中的整数是连续的,那么我们可以说它是一个线性地址空间,为了简化讨论,假设我们使用都是线性地址空间,在一个带虚拟内存的系统中,CPU从一个有N=2^n个地址中生成虚拟地址,这个地址被称为虚拟地址空间。

        一个地址空间的大小是由表示最大地址所需要的位数来描述的,例如一个包含N=2^n个地址的虚拟地址空间就叫做一个n位地址空间,现代系统通常支持32位或64位虚拟地址空间。

        一个系统还有一个物理地址空间,对应系统中物理内存的M个字节,{0,1,2......M-1},M不要求是2的幂,但是为了简化讨论,假设M=2^m。

        地址空间的概念是很重要的,它区分了数据对象(字节)和它们的属性(地址),一旦有这种区别,这就允许每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间,这就是虚拟内存的基本思想。主存中每字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。

3、虚拟内存作为缓存的工具

        概念上而言,虚拟内存被组织为一个由寻访在磁盘上的N个连续的字节大小的单元组成的数组,每字节都有一个唯一的虚拟地址,作为数组的索引,磁盘上数组的内容被缓存在主存中,和存储器层次结构中的其他缓存一样,磁盘(较低层)上的数据被分割成块,这些块作为磁盘和主存之间的传输单元,VM系统通过将虚拟内存分割,称为虚拟页(virtual page,VP)的大小固定的块来处理这个问题,每个虚拟页的大小为P=2^p字节,类似的,物理内存被分割成物理页(Physical page),大小也为P字节(物理页也被称为页帧(page frame))。

        在任意时刻,虚拟页面的集合都分为三个不相交的子集:

        *未分配的:VM系统还未分配或者创建的页,未分配的块没有任何数据与他们相关联,因此也就不占用任何磁盘空间。

        *缓存的:当前已缓存在物理内存中的已分配页。

        *未缓存的:未缓存在物理内存中的已分配页。

        下图展示了一个有8个虚拟页的小虚拟内存,虚拟页0和虚拟页3还没有被分配,因此在磁盘上还不存在,虚拟页1、4和6都被缓存在物理内存中,页2、5和7已经被分配了,但是当前并未缓存在主存中。

        

   3.1 DRAM缓存的组织结构

        SRAM缓存表示CPU和主存之间的L1,L2和L3高速缓存,并用DRAM缓存表示虚拟内存系统的缓存,它在主存中缓存虚拟页。在存储层次结构中,DRAM缓存的位置比对它的组织结构有很大影响。

    3.2 页表

        同任何缓存一样,虚拟内存系统必须有某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方,如果是,系统还必须能确定虚拟页放在哪个物理页中,如果不命中,系统必须判定这个虚拟页存放在磁盘的哪个位置,在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM中,替换掉这个牺牲页。

        这些功能是软硬件联合提供的,包括操作系统软件、MMU中的地址翻译和一个存放在物理内存中的页表的数据结构,页表是将虚拟页映射到物理页,每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表,操作系统负责维护页表的内容,以及在磁盘和DRAM之间来回传送页。下图展示一个页表的基本组织结构,页表就是一个页表条目(PTE)的数组,虚拟地址空间的每个页在页表中有一个固定偏移量处都有一个PTE,假设每个PTE都是由一个有效位和一个n位地址字段组成的,有效位表明该虚拟页当前是都被缓存在DRAM中,如果设置了有效位,那么地址字段就表示DRAM中想对应的物理页起始地址,这个物理页中缓存了虚拟页,如果没有设置有效位,那么一个空地址表示这个虚拟页还未被分配,否则,这个地址就指向该虚拟页在磁盘上的起始位置。

        

        上图展示了一个具有8个虚拟页和4个物理页的系统的页表,四个虚拟页(VP1,VP2,VP7,VP4)被缓存在DRAM中,两个页(VP0和VP5)还没有被分配,剩下的页(VP3、VP6)已经被分配但是当前未被缓存,上图中,DRAM缓存是全相连的,所以任意物理页都可以包含任意虚拟页。

    3.3 页命中

        如果CPU要读包含在VP2中的虚拟地址中的一个字时会发生什么。VP2被缓存在DRAM中,使用地址翻译技术,地址翻译硬件将虚拟地址作为一个索引来定位PTE2,并从内存中读取它,因为设置了有效位,那么地址翻译硬件就知道VP2是缓存在内存中,所以使用PTE中的物理内存地址(该地址指向PP1中缓存页的起始位置),构造这个字的物理地址。

        

    3.4 缺页

        在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页,下图展示了在缺页之前的实例页表的状态。CPU引用VP3中的一个字,VP3并没有缓存在DRAM中,地址翻译硬件从内存中读取PTE3,从有效位推断VP3为被缓存,并且触发一个缺页异常,缺页异常调用内核中缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是存放在PP3中的VP4,如果VP4已经被修改了,那么内核就会将它复制回磁盘,无论哪种情况,内核会修改VP4的页表条目,反映出VP4不再缓存在主存这一事实。

        

        接下来,内核从磁盘复制VP3到内存PP3处,更新PTE3,随后返回,当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重新发送到地址翻译硬件,但是现在VP3已经缓存在主存中了,那么页命中也能由地址翻译硬件正常处理了。下图为缺页之后页表的状态。

        

        页从磁盘换入DRAM和从DRAM换出磁盘,一直等待,也就是说,当有不命中发生时,才换入页面的这种策略叫作按需页面调度,当然也有别的策略但是所有现代系统都使用的是按需页面调度。

    3.5 分配页面

        下图展示当操作系统分配一个新的虚拟内存页时对我们示例页表的影响。例如,调用malloc的结果,在这个例子中,VP5的分配过程是在磁盘上创建空间并更新PTE5,使它指向磁盘上这个新创建的页面。

        

    3.6 局部性

        其实虚拟内存的效率不像我们想象中很低,即使不命中的惩罚很大,其实虚拟内存工作的很好,主要归功于局部性。

        尽管在整个运行过程中程序引用的不同页面的总数可能会超过物理内存总的大小,但是局部性原则保证了在任意时刻,程序将趋于在一个较小的活动页面集合上工作,这个集合叫作工作集或者常驻集合,在初始开销,也就是将工作集页面调度到内存中之后,接下来对这个工作集的引用将导致命中,而不会产生额外的磁盘流量。

        只要我们的程序有良好的时间局部性,虚拟内存系统就能工作得相当好,但是不是所有的程序都是这样的,如果工作集的大小超过了物理内存的大小,那么程序将产生一种不幸的状态,叫作抖动,这时页面将不断换进换出,虽然虚拟内存通常是有效的,但是如果一个程序特别慢的话,那么就要考虑是不是发生了抖动。

4、虚拟内存作为内存管理的工具

        操作系统为每个进程提供一个独立的页表,因而也就是一个独立的虚拟地址,如下图展示。下图中进程i的页表将VP1映射到PP2,VP2映射到PP7,进程j的页表将VP1映射到PP7,VP2映射到PP10,多个虚拟页面可以映射到同一个共享物理页面上。

        

        按需页面调度和独立的虚拟地址空间结合,对系统中的内存使用和管理造成了深远的影响,特别的VM简化了链接、加载、代码和数据共享以及应用程序的内存分配。

        简化链接:独立的地址空间允许每个进程的内存映像使用相同的基本格式,而不管代码和数据实际存放在物理内存的何处。例如,一个给定的linux系统上的每个进程使用类似的内存格式,对于64位地址空间,代码段总是从虚拟地址0x400000开始,数据段跟在代码段之后,中间有一段符合要求的对齐空白,栈占用用户进程地址空间最高的部分,并向下生长,这样的一致性极大地简化了链接器的设计与实现,允许链接器生成完全链接的可执行文件,这些可执行文件是独立与物理内存中代码和数据的最终位置的。

        简化加载:虚拟内存还使得容易向内存中加载可执行文件和共享对象文件,要把目标文件中.text和.data节加载到一个新创建的进程中,linux加载器为代码和数据段分配页,把它们标记为无效的(未被缓存的),将页表条目指向目标文件中适当的位置,加载器不从磁盘到内存复制任何数据,在每个页初次被引用时,要么是CPU取指令是引用的,要么是一条正在执行的指令引用一个内存位置时引用的,虚拟内存系统会按照需要自动地调入数据页。

        将一组连续的虚拟页映射到任意一个文件中的任意位置的表示法称作内存映射,linux提供一个称为mmap的系统调用,允许应用程序自己做内存映射。

        简化共享:独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间共享的一致机制,一般而言,每个进程都有自己私有的代码、数据、堆以及栈区域,是不和其它进程共享的,在这种情况下,操作系统创建页,将相对应的虚拟页映射到不连续的物理页面。

        然而在一些情况中,还是需要进程来共享代码和数据,例如每个进程必须调用相同的操作系统内核代码,而每个C程序都会调用C标准库中的程序,比如printf,操作系统通过将不同进程中适当的虚拟页面映射到相同的物理页面,从而安排多个进程共享这部分代码的一个副本,而不是在每个进程都包括单独的内核和C标准库的副本,如上面图所示。

        *简化内存分配:虚拟内存为用户进程提供一个简单的分配额外内存的机制,当一个运行在用户进程中程序要求额外的堆空间(如调用malloc的结果),操作系统分配一个适当的数据(k)个连续的虚拟内存页面,并将它们映射到物理内存中任意位置的k个任意的物理页面,由于页表工作的方式,操作系统没有必要分配k个连续的物理内存页面,页面可以随机的分散在物理内存中。

5、虚拟内存作为内存保护的工具

        任何现代计算机系统必须为操作系统提供手段来控制对内存系统的方式,不应该允许一个用户进程修改它的只读代码段,而且也不应该它读或修改任何与其他进程共享的虚拟页面,除非所有的共享者都显式地允许它这么做。

        就如我们所看,提供独立的地址空间使得区分不同进程的私有内存变得容易,但是地址翻译机制可以以一种自然的方式扩展到更好的访问控制,因为每次CPU生成一个地址时,地址翻译硬件都会读一个PTE,所以通过PTE上添加一些额外的许可位来控制对一个虚拟页面内容的访问十分简单,下图展示了大致的思想:</

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值