操作系统概念学习笔记 第十章 虚拟内存

时间:2020-12-04 00:54:59

背景:

  在许多情况下并不需要将整个程序放到内存中:

    1 程序通常有处理异常错误条件的代码。由于这些错误即使有也很少发生,所以这种代码几乎不执行

    2 数组、链表和表通常分配了比实际所需要更多的内存

    3 程序的某些选项或特点可能很少使用

  能够执行只有部分在内存中的程序会有很多好处:

    1 程序不再受现有的物理内存空间限制。用户可以为一个巨大的虚拟地址空间写程序,简化了编程操作

    2 因为每个用户程序使用了更少的物理内存,所以更多的程序可以同时执行。CPU使用率也增加,而响应时间或周转时间并不增加

    3 由于装入或交换每个用户程序到内存中所需的I/O会更少,用户程序会运行得更快。

  因此,运行一个部分在内存中的程序不但有利于系统也有利于用户。

  虚拟内存将用户逻辑内存与物理内存分开。

  虚拟内存通常采用请求页面调度来实现。请求分段调度也可用来实现虚拟内存。

 

 

请求页面调度:

  请求页面调度系统类似于分页系统加上交换。进程驻留在次级存储器上(通常为磁盘),当需要执行进程时,使用懒交换(lazy swapper),只有在需要页时,才将它调入内存。

  交换程序对整个进程进行操作,而调页程序只是对进程的单个页进行操作,因此,在讨论有关请求页面调度时,需要使用调页程序而不是交换程序。

  基本概念:

    当换入进程时,调页程序推测在该进程再次换出之前会用到哪些页。把必须的页调入内存。

    对这种方案,需要一定形式的硬件支持来区分哪些页在内存里,哪些页在磁盘上。有效-无效位可以用于这一目的。

    有效:表示相关的页既合法也在内存中

    无效:不在进程的逻辑地址空间内或者有效但是在磁盘上

    对于调入内存的页,其页表条目的设置与平常一样。

    对于不在内存的页,其页表条目设置为无效,或包含该页在磁盘上的地址。

    当进程试图访问那些尚未调入到内存的页时,对标记为无效的访问会产生页错误陷阱。

    分页硬件,在通过页表转换地址时,会发现已设置了无效位,会陷入操作系统。这种陷阱是由于操作系统未能将所需的页调入内存,而不是由于试图使用非法内存地址而引起的无效地址错误。处理这种页错误的程序比较简单:

      1 检查进程的页表(通常与PCB一起保存),以确定该引用是合法还是非法的地址访问。

      2 如果引用非法,那么终止进程。如果引用有效但是尚未调入页面,那么现在应调入

      3 找到一个空闲帧

      4 调度一个磁盘操作,以便将所需要的页调入刚分配的帧

      5 当磁盘读操作完成后,修改进程的内部表和页表,以表示该页已在内存中

      6 重新开始因非法地址陷阱而中断的指令。进程现在能访问所需的页,就好像它似乎总在内存中。

    一种极端情况是所有的页都不在内存中,这时就开始执行进程。这种方案称为纯粹请求页面调度:只有在需要时才将页调入内存。

    有的程序单个指令可能访问多个页地内存,可能产生多个页错误。这种情况会产生令人无法接受的系统性能。幸运的是,这种情况极为少见。程序具有引用的局部性,这使得请求页面调度性能较为合理。

    支持请求页面调度的硬件与分页和交换的硬件一样:

      1 页表:该表能够通过有效-无效位或保护位的特定值,将条目设为无效

      2 次级存储器:该次级存储器用来保护不在内存中的页。

    对于一些特殊的错误,有一些方法能完善系统:

      1 微码计算并试图访问两块地两端,在修改之前就知道没有页错误,才可以执行移动

      2 使用临时寄存器来保护重写位置的值。

  请求页面调度的性能

    对于请求页面调度,降低页错误率是非常重要的。否则,无效访问时间会增加,会显著地降低 进程执行。

 

 

进程创建:

  写时复制(copy-on-write):

    这种方法允许父进程与子进程开始时共享同一页面。这些页面标记为写时复制。如果任何一个进程需要对页进行写操作,那么就创建一个共享页的拷贝。

    采用写时复制技术,很显然只有被进程所修改的页才会复制,所有非修改页可为父进程和子进程所共享。

    注意只有可能修改的页才需要标记为写时复制。

    当确定一个页要采用写时复制时,从哪里分配空闲页是很重要的,许多操作系统为这类请求提供了空闲缓冲池。

    操作系统通常采用按需填零的技术以分配这些页:在需要分配之前先填零,因此清除了以前的页内容。

  内存映射文件:

    虚拟内存技术来将文件I/O作为普通内存访问,这称为文件的内存映射,允许一部分虚拟内存与文件逻辑相关联。

 

 

页面置换:

   基本方法:

    如果没有空闲帧,那么就查找当前不在使用的帧,并使之空闲,这样来释放一个帧,将其内容写到交换空间,并改变页表表示该页不在内存中。然后就使用空闲帧来保存进程出错的帧。修改页错误处理程序以包括页置换:

    1 查找所需页在磁盘上的位置

    2 查找一空闲帧

      a 如果有空闲帧,那么就使用它

      b 如果没有空闲帧,那么就使用页置换算法以选择一个“牺牲”帧

      c 将“牺牲”帧的内容写到磁盘上,改变页表和帧表

    3 将所需页读入(新)空闲帧,改变页表和帧表

    4 重启用户进程

  FIFO页置换:

    一个FIFO队列来管理内存中的所有页。

  最优页置换:

    置换那些在最长时间中不会被使用的页。然而最优页置换算法难于实现,因为需要引用串的未来知识。因此最优算法主要用于比较研究。

  LRU页置换:

    置换最长时间没有使用的页,这种方法称为最近最少使用算法。LRU置换为每个页关联该页上次使用的时间,当必须置换一页时,LRU选择最长时间没有使用的页。LRU策略经常用做页置换算法,且被认为很好,主要问题是如何实现LRU置换。可能需要大量硬件支持。

    有两张方法:

      1 计数器:为每个页表项关联一个使用时间域,并为CPU增加一个逻辑时钟或计数器。对每次内存引用,计数器都会增加。每次内存引用时,时钟寄存器的内容会复制到相应页所对应页表项的使用时间域内。置换具有最小时间的页,这种方案需要搜索页表以查找LRU页,且每次内存访问都要写入内存。在页表改变时也必须要保证时间,必须考虑时钟溢出。

      2 堆栈:实现LRU置换的另一个方法是采用页码堆栈。每当引用一个页,该页就从堆栈中删除并放在顶部,这样,堆栈底部总是LRU页,该堆栈可实现为具有头指针和尾指针的双向链表

    每次内存引用都必须更新时钟域或堆栈,如果每次引用都采用中断,以允许软件更新这些数据结构,那么它会使内存引用慢至少10倍

  LRU近似页置换:

    很少有计算机系统能提供足够的硬件来支持真正LRU页置换算法。有的系统不提供任何支持,因此必须使用其他置换算法(如FIFO算法)。然而很多系统都通过引用位方式提供一定的支持。页表内的每项都关联着一个引用位。每当引用一个页时(无论读或写),相应页的引用位就被硬件置位。

    开始,操作系统会将所有引用位都清零。随着用户进程的执行,与引用页相关联的引用位被硬件置位。通过检查引用位,能确定那些用过而那些没用过。这种部分排序信息导致了许多近似LRU算法的页置换算法。

    1 附加引用位算法:

      可以为位于内存中的每个表中的页保留一个8bit的字节。操作系统把每个页的引用位转移到其8bit字节的高位,而将其他位右移,并抛弃最低位。如果将8bit字节作为无符号整数,那么具有最小值的页为LRU页,且可以被置换。

    2 二次机会算法:

      二次机会置换的基本算法是FIFO置换算法。当要选择一个页时,检查其引用位。如果其值为0,那么就直接置换该页。如果引用位为1,那么就给该页第二次机会,并选择下一个FIFO页。当一个页获得第二次机会时,其引用位清零。且其到达时间设为当前时间。因此获得第二次机会的页,在所有其他页置换之前,是不会被置换的。另外,如果一个页经常使用以致于其引用位总是得到设置,那么它就不会被置换。

      一种实现二次机会算法的方法是采用循环队列。用一个指针表示下次要置换哪个页。当需要一个帧时,指针向前移动直到找到一个引用位为0的页。在向前移动时,它将清除引用位。

    3 增强型二次机会算法

      通过将引用位和修改位作为一个有序对来考虑,能增强二次机会算法。有下面四种可能类型:

      1 (0,0)最近没有使用且也没有修改。---用于置换的最佳页

      2 (0,1)最近没有使用但修改过。---不是很好,因为在置换之前需要将页写出到磁盘

      3 (1,0)最近使用过但没有修改---它有可能很快又要被使用

      4 (1,1)最近使用过且修改过---它有可能很快又要被使用,且置换之前需要将页写出到磁盘

      当页需要置换时,每个页都属于这四种类型之一。置换在最低非空类型中所碰到的页,可能要多次搜索整个循环队列。

  基于计数的页置换:

    1 最不经常使用页置换算法:要求置换计数最小的页。

    2 最常使用页置换算法:

    这两种算法的实现都很费时,且不能很好地近似OPT置换算法。

  页缓冲算法:

    系统通常保留一个空闲帧缓冲池。当出现页错误时,会像以前一样选择一个牺牲帧,在牺牲帧写出之前,所需要的页就从缓冲池中读到空闲内存。

 

 

帧分配:

  帧的最小数量:

    所分配的帧不能超过可用帧的数量。也必须分配至少最少数量的帧。

  分配算法:

  全局分配与局部分配:

    全局置换:允许一个进程从所有帧集合中选择一个置取帧,而不管该帧是否已分配给其他进程;一个进程可以从另一个进程中取帧。

    局部置换:要求每个进程仅从其自己的分配帧中进行选择。

    全局置换通常会有更好的系统吞吐量,且更为常用。

 

 

系统颠簸:

  频繁的页调度的行为称做颠簸。如果一个进程在换页上用的时间要多于执行时间,那么这个进程就在颠簸。

  系统颠簸的原因:

    操作系统监视CPU的利用率,如果太低,引入新进程,新进程获得帧,其他进程页错误增加,排队等待换页设备,CPU利用率降低,操作系统引入新进程。

  工作集合模型:

    工作集合模型是基于局部的假设。该模型使用参数△定义工作集合窗口。其思想是检查最近△个页的引用,这最近△个引用的页集合称为工作集合。如果一个页正在使用中,那么它就在工作集合内,因此,工作集合是程序局部的近似。

    工作集合精确度与△的选择有关。

    工作集合模型的使用较为简单,操作系统跟踪每个进程的工作集合,并为进程分配大于其工作集合的帧数。

  页错误频率:

    为所期望的页错误率设置上限和下限,如果实际页错误率超过上限,那么为进程分配更多帧,如果实际页错误率低于下限,那么可从该进程中移走帧。

 

 

 

操作系统样例

 

 

其他考虑:

  预约式页面调度

    同时将所需的所有页一起调入内存中。

  页大小:

  TLB范围:

  反向页表:

  程序结构:

  I/O互锁:

  实时处理: