• 2020 MIT6.s081 Page Fault & Lazy Page Allocation学习笔记


    引起Page Fault的原因有很多种,其中一个很常见的原因就是Lazy Page Allocation,本文主要介绍其中几种Lazy Page Allocation方式。


    术语

    1. eager: 饿汉方式申请物理内存,即在需要时马上申请。
    2. lazy: 懒汉方式申请物理内存,即在用到时才申请。

    Zero Fill On Demand

    1. 用户空间存在三个区域

      1. data: 已经被初始化的全局变量

      2. text: 程序的指令

      3. bss: 未被初始化和初始化为0的全局变量

    2. bss段的值都为0,且bss段可能占用大量的page,如果在程序启动时我们就为bss分配物理页,则会减慢程序启动的速度,且如果我们一直未用到bss里的数据,就会导致大量的物理内存浪费。

    3. 一个解决方法就是zero-fill-on-demand:刚开始时,将所有的bss页映射至一个物理地址(只读不可写)。如果后续需要为bss里的变量执行写操作,将其变为不为0的值,则新创建一个物理页,并将对应的BSS映射到新的物理页。这也是懒加载的一种体现。

    4. 优点:减少内存使用量,提升程序的启动速度;缺点:会增加发生page fault的几率(也是时间换空间的一种体现)。


    Copy On Write Fork

    1. 一般情况下,在我们用shell执行命令时,会涉及两个主要的系统调用,fork和execfork会将父进程的地址空间拷贝至子进程,子进程在执行exec之前,会将当前的地址空间丢弃,并重新加载新程序,重新生成地址空间。
    2. 如果我们采用eager的方式方式执行上面的过程,则在fork时会申请内存空间、拷贝父进程的物理内存、映射内存空间等操作,在exec时又将fork申请的内存空间进行释放。这其实会浪费资源。
    3. 如果我们采用懒汉的方式执行上面的过程,则过程如下:
      1. 在执行fork之后,子进程并不会申请新的物理内存,而是将自己虚拟空间中的PTE指向对应父进程的物理地址(为方便叙述,之后将这部分内存称为共享内存)。
      2. 如果子进程需要读共享内存,则正常读即可,如果子进程需要写物理内存,则此时会产生page fault,并进行真正意义上的物理内存分配,并将对应的物理内存拷贝至新分配的内存里。
    4. 注意:一个物理页可能同时会有多个进程的虚拟地址与其映射,因此需要为每一份物理内存设置一个引用计数器,只有该物理地址的引用计数器为0时,才能将其释放。

    Demand Paging

    1. 目前在XV6中,exec的加载程序时eager加载的方式,但是实际上我们可以使用懒加载的方式。
    2. 即在开始时,并不分配物理内存,只是将程序的各个段位置设置好(data、text、bss),并在某个地方记录好相应page对应的程序文件。
    3. 当发生缺页中断时,此时才将对应位置的程序拷贝至物理内存,并设置好对应的映射。
    4. 这些在加载时才分配页的技术就叫做Demand Paging.
    5. 优点:某些二进制程序可能特别大,或者操作系统需同时执行大量的程序,此时若采用eager的方式进行物理页的分配,很容易产生OOM的问题,采用Demand Paging可以很好的解决这个问题。缺点:会产生缺页中断,影响程序的执行效率。
    6. 问题:如果系统的物理内存不够了,此时怎么办呢?此时我们可以采用回撤页的方式,即先将一部分物理内存换页换出去。换页基于的策略有:
      1. LRU,即最近最少使用的页换出去;
      2. 基于a的优化,即在a的基础上,换页时进行相关的考虑。若有两种类型的页面,一种是Dirty Page(即最近被写过的页),一种是No DirtyPage(即最近没被写过的页,但是可能被读过),那么换页的时候推荐将No-DirtyPage换出去。备注:在PTE里,有10个标志位,这10个标志位标志了页面的一些属性,比如D(页面是否为Diryty Page),比如A(页面最近是否被访问过)。

    Memory Mapped Files

    采用MMap的技术将文件映射到进程的虚拟内存里,这样读写文件会很快,因为省去了很多内核态和用户态之间的上下文切换(读写文件都走的系统调用,都需要进行内核态和用户态之间的反复切换)。

    1. 这里的映射也是懒加载的方式;
    2. 在操作完成之后,会将Dirtyblock写回文件。

    寄存器辅助调试

    Page Fault里,我们可以借助以下三个寄存器排查问题:

    SEPC:程序出错时用户程序计数器的值,表明发生错误时,用户空间的位置。

    STVAL: 引起出错时的内存地址。

    SCAUSE: 程序出错的原因。


    参考链接

    https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081

    https://pdos.csail.mit.edu/6.S081/2020/schedule.html

  • 相关阅读:
    Linux DRM modesetting API分析
    java-php-net-python-税务申报系统ssh计算机毕业设计程序
    Redis源码学习总结一
    找不到模块“./App.vue”或其相应的类型声明。ts(2307)
    Spring Bean作用域简介说明
    会话跟踪及常用方法
    OpenAI“VoiceEngine”震撼来袭,深度合成算法备案需抓紧
    笔记:Python 字符串与正则表达式(练习题)
    安卓APP源码和设计报告——体育馆预约系统
    计算机网络重点概念整理-第四章 网络层【期末复习|考研复习】
  • 原文地址:https://blog.csdn.net/u014110320/article/details/125994507