美文网首页
CSAPP : 虚拟内存

CSAPP : 虚拟内存

作者: leon4ever | 来源:发表于2017-12-27 21:51 被阅读146次

    本章我们学习操作系统中最重要的概念之一,虚拟内存。

    物理和虚拟寻址

    CPU访问内存的方式,最原始也是最直接的当然是物理寻址,即便是现在,在简单的嵌入式微计算机中,内存也不复杂,所以仍然采用物理寻址。
    虚拟寻址,顾名思义,CPU通过一个虚拟地址(Virtual Address)来访问主存,那么中间这个地址翻译的过程就需要专门的硬件——位于CPU上的内存管理单元(MMU)来搞定了。


    一个使用虚拟寻址的系统.jpg

    使用虚拟内存主要是基于下面三个考虑:

    1. 更有效的使用内存
    2. 简化内存管理:每个进程都有统一的线性地址空间
    3. 隔离地址控件:进程之间不会相互影响;用户程序不能访问内核信息和代码

    虚拟内存的三个角色

    CSAPP中介绍虚拟内存的三个主要角色,相比现代操作系统里直接撸概念要更有方向性:

    1. 作为缓存的工具
    2. 作为内存管理的工具
    3. 作为内存保护的工具

    下面依次介绍

    1. 作为缓存工具

    一个VM系统是如何使用主存作为缓存的.jpg

    虚拟内存被分割为被称为虚拟页(page)的大小固定的块,物理内存当然分割为物理页啦(也叫页帧page frame)。
    大致的思路和之前的 cache memory 是类似的,就是利用 DRAM 比较快的特性,把最常用的数据换缓存起来。如果要访问磁盘的话,大约会比访问 DRAM 慢一万倍,所以我们的目标就是尽可能从 DRAM 中拿数据,所以要做到:

    • 页尺寸(page size):通常是 4KB,有的时候可以达到 4MB
    • 全相联(Fully associative):每一个虚拟页(virual page)可以放在任意的物理页(physical page)中,没有限制。
    • 因为访问磁盘慢,所以总是采用写回

    Write-through: 命中后更新缓存,同时写入到内存中
    Write-back: 直到这个缓存需要被置换出去,才写入到内存中(需要额外的 dirty bit 来表示缓存中的数据是否和内存中相同,因为可能在其他的时候内存中对应地址的数据已经更新,那么重复写入就会导致原有数据丢失)

    操作系统为了对虚拟内存管理,需要一个类似目录的东西,这个数据结构叫做页表(page table),存放在物理内存中,每次地址翻译都要读取页表,操作系统负责维护更新页表。
    要注意,每个进程都有一个独立的虚拟地址空间,所以每个进程也都有自己的页表哦。

    页表.jpg
    那么当操作系统查询页表的时候,会有两种情况出现:
    一种是命中,也就是已缓存,而另外一种是未命中,也就是你要的数据还在磁盘上呢,此时会触发一个缺页异常,调用内核的缺页异常处理程序,替换一个在物理内存中的页。这样你需要的数据就真正放在内存中啦。

    因为局部性原理,程序将趋向于在一个较小的活动页面集合上工作(也就是工作集/常驻集合),所以实际上页面调度不会太低效的。

    2. 作为内存管理工具

    前面提到,每个进程都有自己的虚拟地址空间,这样一来,对于进程来说,它们看到的就是简单的线性空间(但实际上在物理内存中可能是间隔、支离破碎的),具体的映射过程可以用下图表示:


    进程独立的地址空间.jpg

    注意:多个虚拟页面可以映射到同一个共享物理页面上

    按需页面调度和独立的虚拟地址空间的结合,对系统中内存的使用和管理影响深远,具体如下:

    1. 简化链接:每个进程的内存格式都类似,简化链接器的设计和实现
    2. 简化加载:很容易向内存中加载可执行文件和共享对象文件,linux加载器只需要为代码和数据段分配虚拟页,然后标记成未缓存的,将页表条目指向目标文件的位置。具体将数据从磁盘复制到内存的工作则在具体执行指令时CPU引用。
    3. 简化共享:将不同进程中适当的虚拟页面映射到相同的物理页面,就能使多个进程共享操作系统内核代码啦(如printf),而不用每个进程自己都搞一个副本
    4. 简化内存分配:页表的存在,使得操作系统不需要分配连续的物理内存不需要是连续的

    3. 作为内存保护的工具

    页表项中有许可位,MMU通过检查这些位来进行权限控制(读、写、执行)。
    如果指令违反,那么就会报告段错误(segmentation fault)。


    虚拟内存提供页面级内存保护.png

    地址翻译

    先看图


    地址翻译.jpg

    页表基址寄存器是CPU中的一个控制寄存器,也就是用来放页表的地址的。
    地址翻译又根据是否命中分为两种情况:

    页面命中

    1. 处理器生成一个虚拟地址,传送给MMU
    2. MMU生成页表项地址,并从主存请求
    3. 主存向MMU返回页表项内容
    4. MMU构造物理地址,并把这物理地址传送给主存
    5. 主存返回所请求的数据给处理器


      页面命中.jpg

    缺页

    1. 第一步到第三步,与上面相同
    2. 页表项中有效位是零,所以MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序
    3. 缺页处理程序确定出物理内存中的牺牲页,如果修改过,写回磁盘
    4. 缺页处理程序调入新的页面,更新内存中的页表项
    5. 缺页处理程序返回到原来的进程,再次执行导致缺页的指令


      缺页.jpg

    TLB加速地址翻译

    每次CPU都让MMU查页表项也太烦了,不如再搞个缓存,同样利用局部性原理,对这个地址翻译的过程加速一下。于是就有了TLB(Translation Lookaside Buffer),也叫快表,这个东西直接在MMU里的,反正就是快。

    多级页表

    虽然页表是一个表,但因为往往虚拟地址的位数比物理内存的位数要大得多,所以保存页表项(PTE) 所需要的空间也是一个问题。举个例子:

    假设每个页的大小是 4KB(2 的 12 次方),每个地址有 48 位,一条 PTE 记录有 8 个字节,那么要全部保存下来,需要的大小是:


    公式.png

    整整 512 GB!所以我们采用多层页表,第一层的页表中的条目指向第二层的页表,一个一个索引下去,最终寻找具体的物理地址,整个翻译过程如下:


    k级页表的地址翻译.jpg

    具体的地址翻译过程我猜就不用掌握了吧。。。躺。。。

    总之,先总结这些,后面内存映射单独拿出来写,动态内存分配和垃圾收集就放到Malloc Lab里好了。

    相关文章

      网友评论

          本文标题:CSAPP : 虚拟内存

          本文链接:https://www.haomeiwen.com/subject/mtdpgxtx.html