操作系统:虚拟内存管理

虚拟内存允许执行进程不必完全处于内存

一、背景

虚拟内存是内存管理的一种技术,它允许执行进程时不必完全载入内存,可以部分程序载入到内存

优点:

  1. 逻辑地址空间可大于物理地址空间
  2. 可以被多个进程共享地址空间
  3. 可以提供更有效的进程创建

虚拟内存将用户逻辑内存和物理内存分开,这在现有物理内存有限的情况下,为程序员提供了巨大的虚拟内存。
在这里插入图片描述

虚拟内存大于物理内存的图例

进程的虚拟地址空间就是进程如何在内存中存放的逻辑视图。通常从逻辑地址(如地址0)开始,连续存放。

在这里插入图片描述
注意,上图为虚拟地址空间,上图中,随着动态内存的分配,允许堆向上生长;随着子程序的不断调用,允许堆栈向下生长。
堆与堆栈之间的巨大空白空间为虚拟地址的一部分,只有在堆与堆栈生长时,才需要实际的物理页

在这里插入图片描述
虚拟内存也允许共享,实现进程之间的内存共享

二、请求调页

仅在需要时加载页面,这种技术称为请求调页

1.基本概念

调度程序需要一定的硬件支持,以区分内存页面和磁盘的页面。有效-无效位方案可以解决

  • 有效(v):页面在内存中
  • 无效(i):页面不在进程逻辑地址空间中,或有效但只在磁盘上

如果进程试图访问那些尚未调入内存的页面时,对标记为无效的页面访问会产生缺页错误

处理这种缺页错误的程序很简单:

  1. 检查进程内部表,已确定该引用是有效的还是无效的
  2. 如果非法,终止进程;如果有效但尚未调入页面,那么应该调入
  3. 找到一个空闲帧
  4. 将所需要的页调入到,找到的空闲帧里
  5. 修改页表(有效、无效位),以表示该页已在物理内存中
  6. 重启被陷阱中断的指令(重新访问)
    在这里插入图片描述

三、写时复制

写时复制(cow)通过允许父进程和子进程最初共享相同的页面来工作。这些共享页标记为写时复制,这意味着如果任何一个进程需要对页进行写操作,那么就会创建一个共享页的副本。

优点:

  1. 可以快速创建进程
  2. 最小化创建新进程的页数

在这里插入图片描述

进程1修改页面C之前

在这里插入图片描述

进程1修改页面C之后

四、页面置换

如果增加了多道程序,那么可能会过度分配内存
内存的过度分配会有问题。当用户进程正在执行时,可能发生页面错误。操作系统确定所需页面的磁盘位置,但却发现空闲帧列表上没有空闲帧

怎么办

  1. 终止进程
  2. 交换出一个进程,页面置换(page replacement)

1.基本页面置换

如果没有空闲帧,就查找当前不在使用的一个帧,并释放它。

修改缺失错误处理程序,以包括页面置换:

  1. 找到所需页面的磁盘位置
  2. 找到一个空闲帧
    如果有,就是用它
    如果没有,使用页面置换算法来选择一个牺牲帧
    将牺牲帧写回磁盘,修改页表和帧表
  3. 将所需页读入空闲帧,修改页表和帧表
  4. 从发生缺页错误位置,继续用户进程
    在这里插入图片描述

页面置换发生两次页传输(换入、换出),导致页处理时间加倍,增加了内存访问时间

如何解决

  1. 方法一(换出):
    每个页关联一个修改位
    通过修改位确认关联页是否被修改。如果被修改,换出时必需写会磁盘;如果没有,则不需要写回磁盘,从而避免了写入磁盘操作
  2. 方法二(换入):
    系统保留一个空闲帧缓冲池,当需要牺牲帧写回磁盘时,在这之前,从空闲帧缓冲池得到内存空间,先将所需要的页读入内存,这样就可以提前执行用户进程(先分配后换出

页置换算法要考虑的问题
如何最小化页错误的发生,同一个页有可能多次被释放、被载入

利用引用串来评估一个算法:

  1. 引用串:一系列页的序号
  2. 评估:检查发生的页错误次数

随着帧数量的增加,缺页错误的数量会降低至最小值

2.FIFO页面置换

即先进先出。

假设有3个帧,以及引用串为:

70120304230321201701

有下图
在这里插入图片描述
为说明使用FIFO算法可能出现的问题,假设如下引用串:

123412512345

则有:
在这里插入图片描述
上图中,假设有3个帧,则可算出缺页错误数为9(最开始向空帧写入也算一次);但假设有4个帧,则缺页错误数为10 !!
这显然违背了上文提到的帧数和缺页错误数的关系
这种意想不到的结果称为Belady异常

3.最优页面置换

置换将来最长时间不会使用的页面
但问题是我们并不能知道引用串未来的信息,所以最优算法主要用于比较研究

4.LRU页面置换

最近最少使用算法:每个页关联该页上次使用的时间,选择最长时间没有使用的帧
在这里插入图片描述

它的主要问题是:如何实现LRU置换

  1. 计数器
    页表的每一项与计数器相连,并计入时间, 但可能会出现如下问题:加了访问操作(需记录时间)、增加内存使用、每次置换需要搜索全部页表

  2. 每当引用一个页,该页就移动到栈的最顶部,其他依次往下移动,最近不常用的栈放在栈的最低端。采用栈实现方法需要每次更新栈,需要栈中项的移动

5.近似LRU页面置换

1.附加引用位算法
每个页都与引用位相关联

  • 每当引用页时,相应页的引用位就被硬件置位
  • 开始,引用位被初始化为0
  • 页被引用,引用位被设置为1; 没被引用,就设置为0(在规定的时间周期)
  • 如一个8位的字节的引用位表示对8个周期进行记录
  • 每次引用的记录,放到8位字节的最高位,而将其他位向右移一位,并抛弃最低位
  • 每个页都有自己的引用位的值,哪个引用位的值最小,就替换哪个

在这里插入图片描述

2.第二次机会算法
基本算法是一种FIFO置换算法,然鹅,每当需要置换页时,检查每页相关联的引用位

  1. 如引用位为0就置换,并把引用位设置成1
  2. 如引用位为1就跳过(给第二次机会),并清零,然后跳到下一个FIFO页

在这里插入图片描述
3.增强型第二次机会算法
利用2个位,即引用位和修改位

  • 第一位表示是否被引用过
  • 第二位表示是否被修改过

采用这两个位,有以下可能类型:

  • (0, 0)最近没有使用且也没有修改,用于置换的最佳页
  • (0, 1)最近没有使用但修改过,不太好的置换,需要写出到磁盘
  • (1, 0)最近使用过但没有修改,有可能很快又要被使用
  • (1, 1)最近使用过且修改过,有可能很快又要被使用,且置换时需要写出到磁盘。

6.基于计数的页面置换

保留一个用于记录其引用次数的计数器。

有些以下两种方案:

  1. 最不经常使用页置换算法
    理由:经常活动的页应该有更大的引用次数
  2. 最常使用页置换算法
    理由:引用次数少的页可能是刚刚调进来的,将来可能经常用

五、帧分配

每个进程需要分配最小帧数

1.分配算法

  1. 平均分配方式:每个进程分配物理帧的大小相同
  2. 比例分配方式:根据进程大小比例
  3. 优先级分配方式:根据进程优先级分配

2.全局分配和局部分配

页面置换算法分为两大类:全局置换局部置换

  1. 全局置换:从所有帧集中选择一个置换帧
    由于可能会置换其他进程的帧,所以进程分配到的帧数量可能发生变化
  2. 局部置换:仅从自己的分配帧中选择一个置换帧
    分配到的帧数量不会发生变化

六、系统抖动(系统颠簸)

因一个进程没有分配到“足够”的页帧,而频繁的发生页错误,这会导致:

  1. CPU 使用率下降,操作系统会试图增加多道程序的程度
  2. 进程会试图去抢别的进程的帧

在这里插入图片描述
随着多道程序的增加,CPU利用率也增加,直到达到最大值(内存被充分利用),如果多道程序还要增加,那么系统抖动开始,CPU利用率急剧下降

七、内存映射文件

采用虚拟内存技术,以将文件I/O作为常规内存访问,这种方法称为内存映射,允许一部分虚拟内存与文件进行逻辑管理

1.基本机制

将磁盘块儿(block)映射到内存的一页或多页

  • 最开始,访问文件会发生页错误
  • 文件的读写就按通常的内存访问来处理
  • 文件的写(磁盘)I/O 操作不一定立即发生,而是定期的发生或关闭文件时发生

优点: 文件共享
多个进程可以将同一个文件映射到各自的虚拟内存中,以允许数据共享

在这里插入图片描述

八、分配内核内存

用于分配内核内存的空闲内存池通常不同于普通用户模式进程的列表

  1. 内核需要为不同大小数据结构请求内存,并努力最小化碎片浪费
  2. 需要连续分配

下面讨论两个策略,以便管理用于内核进程的空闲内存。

1.伙伴系统

buddy系统:从物理上连续的、大小固定的段上进行分配
内存按2的幂的大小来进行分配(2,4,8,16…),直到分配合适的页为止。

在这里插入图片描述
优点:利用合并技术可将相邻伙伴快速组成更大分段
缺点:造成分配段内的碎片

2.slab系统

每个slab是由一个或多个物理上连续的页组成。
每个cache含有一个或多个slab

  1. 每个内核数据结构(信号量,文件对象,进程描述符等)都有它的cache,每个cache 含有内核数据结构的对象实例
  2. 当创建cache 时, 起初包含若干标记为空闲的对象,对象的数量与slab 的大小关联
  3. 当需要内核数据结构的对象时,可从cache中获取,并标记为使用

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44759105/article/details/111715001