OS2️⃣内存管理:虚拟内存

内存(RAM):计算机的重要资源,需要重点管理。

  • 理想情况:私有、容量无限大、速度无限快、价格低廉。
  • 目前技术无法同时满足以上特点,因此诞生了分层存储器体系

1、无存储器抽象

早期的存储器没有抽象,程序直接访问物理内存

通常,同一时刻只允许一个进程运行。

  1. 用户输入命令后,OS 把需要的程序从磁盘复制到内存中执行;
  2. 进程运行结束后,OS 等待新的命令;
  3. 收到新的命令后,OS 载入新的程序(覆盖上一个程序)。

没有抽象,如何运行多个程序?

  • 交换(swap):OS 将当前内存的程序保存到磁盘文件中,将新的程序读入内存中执行。
  • 分块:将内存划分为多个同等大小的块,基于绝对物理地址进行内存寻址。

2、地址空间

内存管理需要解决的问题:保护、重定位。

2.1、概念

地址空间:每个线程独有的,独立于其它线程的,可用于寻址内存的一套地址集合。

  • 物理地址空间:硬件支持的地址空间。
  • 逻辑地址空间:进程所拥有的内存范围。

如何实现每个进程独有地址空间?

动态重定位:将进程的地址空间映射到物理内存的不同部分。

  • 经典实现:每个 CPU 配置基址寄存器和界限寄存器。
  • 过程:当进程访问一个内存地址(x)时,CPU 硬件会将基址值加到内存地址上,并检查是否超过界限寄存器的值,是则产生异常。
  • 缺点:每次访问内存都要进行加法、比较运算。

2.2、内存使用管理

通常,OS 有两种方法跟踪内存使用情况。

  • 位图:将内存划分为多个同等大小的分配单元,每个单元对应位图中的一位。
  • 链表:定义一个链表节点(包含空闲区/进程标志,起始地址,长度,next 域),按照地址顺序形成链表。

2.3、内存碎片

内存碎片(空闲区):无法被利用的内存空间,由于内存分配或交换导致。

  • 外部碎片:分配单元之间。
  • 内部碎片:分配单元内部。

2.4、内存分配方法

假设

  1. 进程和空闲区按照地址顺序记录在链表。
  2. 存储管理器已知要为进程分配的内存大小。

常见的内存分配方法

过程 优点 缺点
首次适配(first fit) 沿着链表从头搜索,找到首个合适的空闲区(足以容纳当前程序) 最简单,速度快 外部碎片
下次适配(next fit) 在首次适配的基础上,记录当前位置。之后每次从记录位置开始搜索 优化 first fit 的查找效率 同上
最佳适配(best fit) 沿着链表从头搜索,找到能够容纳进程的最小空闲区 内存需求小则高效 更小的外部碎片,重分配慢
最差适配(worst fit) 沿着链表从头搜索,找到能够容纳进程的最大空闲区。 内存需求适中则搞笑 外部碎片,重分配慢
快速适配(quick fit) 为常用大小的空闲区维护单独的链表

2.5、碎片整理方法

内存紧缩:重置程序以将进程尽可能向内存的一侧移动,合并碎片。

  • 要求:所有程序动态可重置。
  • 问题:只能在程序处于等待状态时进行,CPU 开销大。

2.6、技术

在内存无法满足进程使用的情况下,可考虑覆盖和交换技术。

2.6.1、覆盖(overlay)

场景:程序太大

思路:将程序按照逻辑结构,划分为功能独立的若干个模块,不会同时执行的模块共享同一块内存空间。

  • 常用功能(必要)的代码和数据需要常驻内存;不常用功能(可选)可存放在外存中,需要时调入内存。
  • 存在相互调用关系的模块才需要同时装入内存;不存在相互调用关系的模块可以按时间顺序执行,相互覆盖。

缺点

  1. 编程复杂:需要确定模块之间的覆盖关系,手动划分为若干个功能模块。
  2. 耗时:覆盖的本质是将模块从外存读入内存,牺牲时间换取空间。

2.6.2、交换(swapping)

场景:程序太多

  • 思路:将内存中暂时无法运行的程序整个保存到外存中(swap out),将外存中某个进程的地址空间读入到内存(swap in)
  • 问题
    • 交换时机:内存不足。
    • 交换区大小:足以存放所有用户进程的所有内存映像。
    • 换入时的重定位:换出后再换入后未必是原来的内存位置,最好采用动态地址映射。

3、虚拟内存

内存管理的另一个问题:软件的膨胀。

软件大小的增长速度,远远超过了存储器容量的增长速度。

3.1、非连续内存分配

  • 连续内存分配:OS 为进程分配内存时,分配一块连续的地址空间。
    • 缺点:容易产生内存碎片,内存利用率低。
  • 非连续内存分配:为进程分配的地址空间是非连续的。
    • 优点:提高内存利用率,允许共享代码和数据,支持动态加载、动态链接。
    • 缺点:建立虚拟地址和物理地址的映射难度大。

3.2、虚拟内存

3.2.1、内存映射

进程执行指令时会产生虚拟地址

  1. 没有虚拟内存:虚拟地址被 OS 直接送到内存总线上访问相应的物理内存。

    (虚拟地址 = 物理地址)

  2. 使用虚拟内存:虚拟地址被 OS 送到 MMU(内存管理单元),MMU 将虚拟地址映射为物理内存地址,再将映射之后的地址送到内存总线。

3.2.2、基本思想

每个程序拥有独立的虚拟地址空间,地址空间被分割成多个页(page)

  • 每个 page 有连续的地址范围。
  • page 被映射到物理内存。
  • 并非程序的所有 page 都在内存中才可运行程序。

当程序访问地址空间时,根据其是否在物理内存中:

  • :执行内存映射并访问。
  • :产生缺页中断,由 OS 将缺失的页装入物理内存,重新执行导致缺页中断的失败的指令。

3.3、分页(Paging)

  • 分页
    • page(页):虚拟地址空间被划分成大小相等的 page。
    • page frame(页帧,页框):物理内存空间被划分成大小相等的 Page frame。
  • 关系
    • 虚拟内存的 page 映射到物理内存中的 page frame。
    • page 和 page frame 的大小通常相等。
    • page 是连续的,page frame 是非连续的(减少碎片产生)。
    • 虚拟内存空间 > 物理内存空间,并非所有 page 都有对应的 page frame(物理内存有限)。

3.4、页表(Page Table)

页表

  • 维护 page 和 page frame 的映射关系。
  • 每个进程有一个独立的页表,属于程序运行状态,会动态变化。

3.4.1、映射过程

  1. 虚拟地址划分为虚拟页号(pageNo)和偏移量(offset)。
  2. 通过 pageNo 计算页表项(也可直接将虚拟页号作为页表的 index)
  3. 通过页表项确定 pageNo 对应的页帧号(frameNo),得出真实的物理地址(offset + pageNo)。

3.4.2、页表项结构

说明 备注
页帧号 虚拟页号映射的结果
驻留位 标识页表项是否有效(即对应的页面是否在内存中) 0-无效:访问时会引起缺页中断
保护位 页面允许的访问类型 使用一位:0-可读写,1-只读
使用三位:读、写、执行
访问位 标识页面是否被读/写过 作为缺页中断时页面置换的辅助
修改位 标识页面是否被修改过(脏页) 1-脏:必须写回磁盘

3.5、分页性能

分页机制需要考虑的性能问题

  1. 时间
    • 每次访问内存需要进行至少 2 次内存访问(访问页表项、访问内存)
    • 从虚拟地址到物理地址的映射速度必须快。
  2. 空间
    • 每个进程需要一个独立的页表。
    • 虚拟地址空间增大会导致页表增大。

3.5.1、加速分页过程

① 转换检测缓冲区(TLB)

转换检测缓冲区(Translation Lookaside Buffer)

又称为 快表,相联存储器(associate memory)

  • 说明
    • 在分页机制下,进程需要先访问页表才能实现内存访问,性能下降至少一半。
    • 局部性访问:在进程运行期间,只访问少部分页面(而不是全部),因此只有少数页表项会被反复读取。
  • TLB:为 CPU 设置的一个小型硬件设备(TLB),通常位于 MMU 中,包含少量表项。
  • 表项结构:虚拟页号、页帧号(frameNo)、驻留位、修改位、保护位。
  • 工作原理
    1. 进程将虚拟地址发送给 MMU。
    2. MMU 将虚拟页号与 TLB 所有表项同时匹配(并行)
      • 有效匹配且不违反保护位:将 frameNo 从 TLB 中取出,无需访问页表。
      • 有效匹配但违反保护位:产生保护错误。
      • 无效匹配:进行正常的页表查询,并将新找到的页表项取代 TLB 中的一个表项,TLB 中被淘汰的页表项的修改位会被写回内存。

② 软件 TLB 管理

  • 过去:对 TLB 的管理和失效处理都由 MMU 硬件实现,只有当发生缺页中断时才会由软件(OS)处理。
  • 现在:由软件实现几乎所有的页面管理。
    • TLB 表项被 OS 装载。
    • 发生 TLB 访问失效时,由 OS 取代 MMU 硬件完成失效处理(页表查询,淘汰一个 TLB 表项,装载新项,再次执行产生出错的指令)

失效

软失效 硬失效
含义 页面不在 TLB 中,但在内存中 页面不在 TLB 中,也不在内存中
解决 更新 TLB(无需产生磁盘 I/O) 进行一次磁盘 I/O,装入页面

缺页错误

次要缺页 严重缺页 段错误
含义 页面在内存中,但未记录在该进程的页表中 页面不在内存中 进程访问了非法地址
性质 软失效的一种 硬失效 程序错误

3.5.2、针对大内存的页表

① 多级页表

将虚拟页号分为 n 个部分,每个部分对应一个页表(形成树结构)。

(虚拟地址 = n级页号 + 偏移量)

以二级页表为例,虚拟页号分为 2 个部分。

  • 对应关系:一级页号对应一级页表,二级页号对应二级页表。

  • 查找过程

    1. 通过一级页号查一级页表,得到二级页表的起始地址。
    2. 通过二级页号查二级页表,得到页帧号。
    3. 页帧号 + 偏移量 = 物理地址。
  • 好处:节省内存空间。

    (若 k 级页表的某一项的 resident bit = 0,则以该项为根节点的子树都不需要存储页表项。)

② 倒排页表

页表:每个页(page)对应一个页表项,通过虚拟页号查找页帧号。

倒排页表:每个页帧(page frame)对应一个页表项,通过页帧号寻找虚拟页号。

  • 优点:节省大量内存空间
  • 缺点:从虚拟地址到物理地址的映射困难。
    • 虚拟页号无法作为倒排页表的索引。
    • 当进程 n 访问虚拟页面 p 时,必须搜索整个倒排页表。
posted @ 2022-07-21 00:45  Jaywee  阅读(134)  评论(0编辑  收藏  举报

👇