# 3. 内存空间的扩充

## 1. 覆盖技术

覆盖技术用来解决程序大小超过物理内存总和问题

**思想：**&#x5C06;程序分为多个段（多个模块），常用的段常驻内存，不常用的段在需要时调入内存

内存中分为一个**固定区**和**若干个覆盖区**。把要**常驻内存段**放在固定区，**调入后就不再调出**（除非运行结束）；**不常用的段**放在覆盖区，**需要用到时调入内存，用不到时调出内存**

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFKtmgaghifpIKpfbja%2F-MFKtwHU-wBM_vcgIeKC%2Fimage.png?alt=media\&token=6ad5d625-dd8e-438a-bd61-112804e60771)

其中 A 一直运行，A 会调用 B 或 C（不会同时调用），B 会调用 D，C 会调用 E 或 F（不会同时调用）。可知 B、C 不可能同时进入内存，D、E、F 不可能同时进入内存，所以开辟两个覆盖区即可（节约内存）

**必须由程序员声明覆盖结构**，操作系统完成自动覆盖。**缺点：**&#x5BF9;用户不透明，增加了用户编程负担

## 2. 交换技术

**思想：**&#x5185;存空间紧张时，系统将内存中某些进程暂时**换出**外存（[**挂起**](https://lfool.gitbook.io/operating-system/di-er-zhang-jin-cheng-guan-li/6.-jin-cheng-de-tiao-du#22-zhong-ji-tiao-du)），把外存中某些已具备运行条件的进程**换入**内存（进程在内存与磁盘间动态调度）

**三个问题：**

1. 被换出的进程应该保存在外存（磁盘）的什么位置？
   * 磁盘存储空间分为**对换区 & 文件区**
   * **文件区：采用离散分配方式，存放文件（主要追求磁盘空间的利用率）**
   * **对换区：采用连续分配方式，存放换出进程的数据（主要追求换出换入速度）**
   * **对换区的 I/O 速度比文件区更快**
2. 什么时候应该换出？
   * 许多内存运行且内存吃紧时进行，而系统负荷降低就停止
3. 应该换出哪些进程？
   * 优先换出**阻塞进程**
   * 优先换出**优先级低的进程**
   * 有的系统还会考虑**驻留时间**

**注意：PCB 会常驻内存，不会被换出内存，换出的仅仅是进程的数据部分**

## 3. 虚拟存储技术

### 3.1 传统存储管理方式的特征、缺点

* **一次性：作业必须一次性全部装入内存后才能开始运行**
  * 作业很大时，不能全部装入内存，导致**大作业无法运行**
  * 当大量作业要求运行时，由于内存无法容纳所有的作业，因此只有少量作业能运行，导致**多道程序并发度下降**
* **驻留性：**&#x4E00;旦作业被装入内存，就**会一直驻留内存中**，直至作业运行结束
  * 事实上，在一个时间段内，只需要访问作业的一小部分数据即可正常运行

### 3.2 虚拟内存的定义和特征

基于局部性原理，在程序装入时，可以将程序中**很快用到的部分装入内存，暂时用不到的部分留在外存**，就可以让程序开始执行

在程序执行过程中，当所访问的**信息不在内存时**，由**操作系统负责将所需要的信息从外存调入内存**，然后继续执行程序

若内存空间不够时，由**操作系统负责**将内存中**暂时用不到的信息换出到外存**

在操作系统的管理下，在用户看来似乎有一个比实际内存大得多的内存，这就是**虚拟内存**

**特点：**

1. **多次性：**&#x65E0;需在作业运行时一次性全部装入内存，而是运行被分为多次调入内存
2. **兑换性**：在作业运行时无需一直常驻内存，而是允许在作业运行过程中，将作业换入、换出
3. **虚拟性：**&#x4ECE;逻辑上扩充了内存的容量，使用户看到的内存容量，远大于实际容量

### 3.3 实现虚拟内存技术

在传统的非连续分配存储管理的方式下进行改进：[**请求分页存储管理**](#4-qing-qiu-fen-ye-guan-li-fang-shi)**、请求分段存储管理、请求段页式存储管理**

**主要区别：**

* 在程序执行过程中，当所**访问的信息不再内存时，由操作系统负责将所需要信息从外存调入内存**，然后继续执行程序
* 若内存空间不够，由操作系统负责**将内存中暂时用不到的信息换出到外存**

## 4. 请求分页管理方式

### 4.1 页表机制

新增两个功能

* **请求调页：**&#x64CD;作系统需要知道每个页面是否已经调入内存；如果还没调入，那么也需要知道该页面在外存中存放的位置
* **页面置换：**&#x64CD;作系统需要通过某些指标来决定到底换出哪个页面（需要记录页面是否被修改）

添加了四个标志位：**状态位、访问字段、修改位、外存地址**

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFM9XwtAsniJV0yW9FA%2F-MFM9oRmlvjYs3kPGsjC%2Fimage.png?alt=media\&token=d1112334-de2b-414f-8c18-e9a5e1d02907)

### 4.2 缺页中断机构

在请求分页系统中，每当要访问的**页面不在内存**时，便产生一个**缺页中断**，然后由操作系统的缺页**中断处理程序处理中断**

此时**缺页的进程阻塞**，放入阻塞队列，调页**完成后再将其唤醒，**&#x653E;回就绪队列

* 如果内存中**有空闲块**，则为进程**分配一个空闲块**，将所缺页面装入该块，并修改页表中相应的页表项
* 如果内存中**没有空闲块**，则**由页面置换算法选择一个页面淘汰**，若该页面在内存期间**被修改过**，则要将其**写回外存**。未修改过的页面不用写回外存

### 4.3 地址变换机构

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFM9XwtAsniJV0yW9FA%2F-MFMCcRWdL_yocZam3pe%2Fimage.png?alt=media\&token=3e84a847-2ead-4afd-beda-b012ffdbd807)

1. 只有“写指令”才需要修改 “修改位”。并且，一般来说只需修改快表中的数据，只有要将快表项删除时才需要写回内存中的慢表。这样可以减少访存次数
2. 和普通的中断处理一样，缺页中断处理依然需要保留CPU现场
3. 需要用某种“[页面置换算法](#5-ye-mian-zhi-huan-suan-fa)” 来决定一个换出页面
4. 换入/换出页面都需要启动慢速的I/O操作，可见，如果换入/换出太频繁，会有很大的开销
5. 页面调入内存后，需要修改慢表，同时也需要将表项复制到快表中

## 5. 页面置换算法

### 5.1 最佳置换算法（OPT）

每次选择**淘汰的页面**将是**以后永不使用**，或者**在最长时间内不再被使用访问的页面**

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFM9XwtAsniJV0yW9FA%2F-MFMF0N65V6g8sqycL3Z%2Fimage.png?alt=media\&token=4e3a439b-f602-4ef6-bd69-fde7a61aa951)

最佳置换算法可以保证最低的缺页率，但实际上，只有在进程执行的过程中才能知道接下来会访问到 的是哪个页面。操作系统无法提前预判页面访问序列。因此，**最佳置换算法是无法实现的**

### 5.2 先进先出置换算法（FIFO）

每次选择**淘汰的页面**是**最早进入内存的页面**

实现方法：把调入内存的页面根据调入的先后顺序排成-一个队列，需要换出页面时选择队头页面即可。 队列的最大长度取决于系统为进程分配了多少个内存块

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFMFZZZee2jCa2DXOth%2F-MFMG-8mW_Y-O4GsZUHw%2Fimage.png?alt=media\&token=a155a5d8-7fd3-403e-adf7-9a566047e6ad)

**Belady异常：**&#x5F53;为进程分配的内存块增大时，出现缺页次数不减反增的异常现象

**只有FIFO算法会产生Belady异常**。另外，FIFO算法虽然**实现简单**，但是该算法与进程实际运行时的 规律不适应，因为先进入的页面也有可能最经常被访问。因此，**算法性能差**

### 5.3 最近最久未使用置换算法（LRU）

每次**淘汰的页面**是**最近最久未使用的页面**

实现方法：赋予每个页面对应的页表项中，用**访问字段记录该页面上次被访问以来所经历的时间 t**。当需要淘汰一个页面时，选择现有页面中 t 值最大的，即最近最久未使用的页面

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFOzvUo-C7jSOEGzBTn%2F-MFP601ov1BMF0DGQlqb%2Fimage.png?alt=media\&token=a2b4b0dc-148f-48ab-b7c2-155c9e0a07f9)

该算法的实现需要专门的硬件支持，虽然算法**性能好**，但是**现实困难**，**开销大**

### 5.4 时钟置换算法（CLOCK）

改进上述三种算法，最佳置换算法性能最好，但无法实现；先进先出置换算法实现简单，但算法性能差；最近最久未使用置换算法性能好，是最接近最佳置换算法性能的，但是实现需要专门的硬件支持，算法开销大

时钟置换算法是一种性能和开销较均衡的算法，又称 CLOCK 算法，或最近未使用算法（LRN，Not Recently Used）

**实现方法：**&#x4E3A;每个页面设置一个访问位，将内存中页面都通过指针链接形成一个循环队列

* 当某页面被访问时，将其访问位置为 1
* 当需要淘汰一个页面时，只需检查页面的访问位
  * 如果访问位是 0，则选择换出
  * 如果访问位是 1，则将它置为 0，暂不换出，继续检查下一个页面
* 如果第一轮扫描没有符合条件的页面，则继续扫描第二轮（第二轮一定有访问位为 0 的页面）

所以该算法最多扫描两轮

### 5.5 改进型时钟置换算法

简单的时钟置换算法仅仅考虑了是否被访问过，如果一个页面访问过但是未被修改，淘汰时就不需要写回内存，应该优先淘汰此类的页面，可以减少 I/O 操作写回内存

用两个标志位（访问位，修改位）来记录一个页面是否被访问过，是否被修改过。在其它条件都相同的时候，应该优先淘汰没有修改过的页面，避免 I/O 操作

**算法规则：**&#x5C06;所有可能被置换的页面排成一个循环队列

* 第一轮：从当前位置开始扫描到第一个（0，0）的页面用于替换。本轮扫描不修改任何标志&#x4F4D;**（第一优先级：最近没访问，且没修改的页面）**
* 第二轮：若第一轮扫描失败，则重新扫描，查找第一个（0，1）的页面用于替换。本轮所有扫描过的页面访问位设置为 &#x30;**（第二优先级：最近没访问，但修改过的页面）**
* 第三轮：若第二轮扫描失败，则重新扫描，查找第一个（0，0）的页面用于替换。本轮扫描比修改任何标志&#x4F4D;**（第三优先级：最近访问过，但没修改的页面）**
* 第四轮：若第三轮扫描失败，则重新扫描，查找第一个（0，1）的页面用于替&#x6362;**（第四优先级：最近访问过，且修改过的页面）**

### 5.6 对比

|        置换算法        |        算法规则       |                   优缺点                  |
| :----------------: | :---------------: | :------------------------------------: |
|         OPT        | 优先淘汰最长时间内不会被访问的页面 |     <p>缺页率最小，性能最好；</p><p>但无法实现</p>     |
|        FIFO        |   优先淘汰最先进入内存的页面   | <p>实现简单，但性能差；</p><p>可能出现 Belady 异常</p> |
|         LRU        |   优先淘汰最近最久未访问的页面  |    <p>性能很好；</p><p>但需要硬件支持，算法开销大</p>    |
|     CLOCK（NRU）     |    优先淘汰最近未访问的页面   |  <p>实现简单，算法开销小；</p><p>但未考虑页面是否被修改过</p> |
| 改进型 CLOCK（改进型 NRN） |  优先淘汰最近未访问且未修改的页面 |              算法开销较小，性能也不错              |

## 6. 页面分配策略

**驻留集：**&#x8BF7;求分页存储管理中给进程分配的物理块的集合

* 采用虚拟存储技术系统中，驻留集大小一般小于进程总大小
* **若驻留级太小，**&#x4F1A;导致频繁缺页，系统需要花费大量时间来处理缺页
* **若驻留级太大，**&#x4F1A;导致多道程序并发度下降，资源利用率降低

**固定分配：**&#x64CD;作系统为每个进程分配一组固定数目的物理块，在进程运行过程中**驻留集大小不变**

**可变分配：**&#x64CD;作系统先为每个进程分配一定数目的物理块，在进程运行期间，可根据情况做适当的增加或减少，**驻留集大小可变**

**局部置换：**&#x53D1;生缺页时只能选进程自己的物理块进行置换

**全局置换：**&#x53EF;以将操作系统保留的空闲物理块分配给进程，也可以将别的进程持有的物理块置换到外存，再分配给缺页进程

![](https://824295091-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEmUhsvIFBb8ar2-Um3%2F-MFPD0lvOV4TvbU1I3EE%2F-MFPD1dJnf5oFt-Ezu1o%2Fimage.png?alt=media\&token=61b29eb8-e536-4041-8e28-7ccaca5a221e)

### 6.1 页面分配、置换策略

**固定分配局部置换：**&#x7CFB;统为每个进程分配一定数量的物理块，在整个运行期间都不改变。若进程在运行中发生缺页，则只能从该进程在内存中的页面中选出一页换出，然后再调入需要的页面。这种策略的缺点是：很难在刚开始就确定应为每个进程分配多少个物理块才算合理。（采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数）

**可变分配全局置换：**&#x521A;开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时，从空闲物理块中取出一块分配给该进程；若已无空闲物理块，则可选择一个**未锁定**的页面换出外存，再将该物理块分配给缺页的进程。采用这种策略时，**只要某进程发生缺页，都将获得新的物理块**，仅当空闲物理块用完时，系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程中的页，因此这个被选中的进程拥有的物理块会减少，缺页率会增加

可变分配容局置换：刚开始会为每个进程分配一定数量的物理块。当某进程发生缺页时，只允许从该进程的自己的物理块中选出一个进行换出外存。如果进程在运行中频繁地缺页，系统会为该进程多分配几个物理块，直至该进程缺页率趋势适当程度；反之，如果进程在运行中缺页率特别低，则可适当减少分配给该进程的物理块。

**可变分配全局置换：**&#x53EA;要缺页就给分配新物理块

**可变分配局部置换：**&#x8981;根据发生缺页的频率来动态地增加或减少进程的物理块
