1.驻留集大小
在操作系统的存储管理中,驻留集(Resident Set) 是指请求分页存储管理中,给一个进程分配的物理内存块(页框/Frame)的集合。
简单来说,驻留集大小就是当前有多少个页面被实际放在了物理内存里,而不是在磁盘的交换区(Swap)中。
1. 驻留集大小的影响因素
驻留集的大小直接影响系统的性能。操作系统需要在“让进程跑得快”和“内存利用率高”之间寻找平衡点:
- 如果驻留集太小: 进程频繁访问的页面不在内存中,会导致频繁触发缺页中断(Page Fault)。当缺页率高到一定程度,CPU 会花费大量时间在磁盘 I/O 上,导致系统“抖动”(Thrashing)。
- 如果驻留集太大: 物理内存被少数进程霸占,导致内存中能运行的进程数量(多道程序度)减少,降低了系统的并发处理能力。
规律: 随着驻留集增大,缺页率会迅速下降。但当驻留集达到一定规模(通常接近该进程的“工作集”)后,再增加内存块对降低缺页率的效果就微乎其微了。
2. 分配策略与置换策略
在实际管理中,操作系统通常采用以下几种策略来决定驻留集的大小和如何置换页面:
A. 分配方式:
- 固定分配(Fixed Allocation): 在进程创建时分配固定数量的物理块,运行期间保持不变。
- 可变分配(Variable Allocation): 运行期间根据进程的缺页率动态调整物理块的数量。
B. 置换范围:
- 局部置换(Local Replacement): 发生缺页时,只能从该进程自己的驻留集中选出一页换出。
- 全局置换(Global Replacement): 发生缺页时,可以将内存中任何空闲或属于其他进程的页面换出。
C. 常见的组合方式:
| 组合策略 | 说明 |
|---|---|
| 固定分配 + 局部置换 | 很难确定每个进程初始该分多少块,分多了浪费,分少了易抖动。 |
| 可变分配 + 全局置换 | 最常见。OS 维护一个空闲物理块队列,缺页就分一块给进程;内存不足时才挑其他进程的页换出。 |
| 可变分配 + 局部置换 | 效果最好。根据缺页率动态调整:缺页多就增加该进程的驻留集,缺页极少就适当缩减。 |
3. 工作集(Working Set)与驻留集
为了科学地设定驻留集大小,操作系统引入了工作集的概念:
- 工作集: 是进程在某段时间内频繁访问的页面集合(基于局部性原理)。
- 管理目标: 理想状态下,驻留集大小 $\ge$ 工作集大小。如果驻留集小于工作集,进程性能会发生断崖式下跌。
4. 在 Linux 中查看驻留集
在 Linux 系统中,驻留集通常被称为 RSS (Resident Set Size)。你可以通过 top 或 ps 命令查看:
- VIRT (Virtual Size): 虚拟内存大小(进程申请的总地址空间)。
- RES (Resident Size): 实际占用的物理内存(即驻留集大小)。
小贴士: 当你发现一个进程的
RES远小于VIRT时,说明它虽然申请了很多内存,但大部分还在磁盘里或者根本没被使用
2.内存分配策略
1.固定分配局部配置
在请求分页管理系统中,“固定分配局部置换”(Fixed Allocation, Local Replacement)是一种相对基础且保守的内存分配与回收策略。
它的核心逻辑可以概括为:“初始分多少就给多少,缺页了只能在自己家里换。”
1. 核心定义
-
固定分配 (Fixed Allocation):
在进程创建时,操作系统根据进程类型、优先级或程序员的建议,为其分配固定数量的物理块(页框)。在整个进程运行期间,这个数量保持不变。
-
局部置换 (Local Replacement):
当进程在运行中发生缺页时,只能从分配给该进程自己的物理块中选择一页换出,然后再将所需的页面调入。它严禁占用其他进程的物理块。
2. 工作原理
假设系统给进程 A 分配了 3 个物理块,初始状态如下:
- 运行中: 进程 A 占用了物理块 1, 2, 3。
- 发生缺页: 进程 A 访问第 4 页,但内存中只有 1, 2, 3 页。
- 置换过程:
- 操作系统查看进程 A 的 3 个物理块。
- 根据某种算法(如 FIFO 或 LRU)从这 3 个块中挑出一个(假设是第 1 页)换出到磁盘。
- 将第 4 页装入原来物理块 1 的位置。
- 结果: 进程 A 占有的物理块总数依然是 3 个。
3. 该策略的优缺点
| 特点 | 说明 |
|---|---|
| 优点:简单稳定 | 算法实现简单,进程之间相互隔离,一个进程的频繁缺页(抖动)不会抢夺其他进程的资源,保证了系统的局部稳定性。 |
| 缺点 1:难以预估 | “分多少”是个难题。如果分少了,进程会频繁缺页(抖动);如果分多了,会浪费宝贵的内存资源。 |
| 缺点 2:灵活性差 | 无法根据进程在不同阶段的运行需求(如局部性原理的变化)动态调整内存空间。 |
4. 为什么现在很少单独使用?
在现代通用操作系统(如 Windows, Linux)中,纯粹的“固定分配局部置换”并不多见,原因在于:
- 进程的需求是动态的: 很多进程在初始化阶段需要大量内存,而进入平稳运行期后需求会减少。固定分配无法应对这种波动。
- 现代主流: 现代系统更多采用可变分配全局置换(追求整体效率)或可变分配局部置换(追求效率与公平的平衡)。
5. 知识拓展:如何确定“固定”的额度?
如果非要使用这种策略,通常有三种分配方法:
- 平均分配: 总块数 / 进程数(完全不考虑进程大小)。
- 按比例分配: 根据进程的总页面数占系统总页面数的比例来分。
- 按优先级分配: 给核心或高优进程多分配一些。
2.可变分配全局置换
可变分配全局置换(Variable Allocation, Global Replacement) 是目前主流操作系统(如 Windows、Linux)中应用最广泛的一种内存管理策略。
它的核心逻辑可以总结为:“按需索取,能抢别人的。”
1. 核心定义
- 可变分配 (Variable Allocation): OS 给进程分配的物理块数量是不固定的,随着进程运行的需要增加或减少。
- 全局置换 (Global Replacement): 当进程发生缺页时,OS 不仅可以从该进程自己的驻留集中选出一页换出,还可以从系统空闲块队列中取出一块,甚至强行剥夺其他进程占用的物理块。
2. 工作过程
假设系统中正在运行进程 A 和进程 B:
- 初始: 进程 A 占有 3 个物理块,系统有一个空闲物理块队列。
- 缺页发生: 进程 A 需要访问第 5 页,但不在内存中。
- 置换选择:
- 优先方案: 如果系统空闲队列里有块,OS 直接分给进程 A(此时进程 A 驻留集由 3 变为 4)。
- 强制方案: 如果系统没有空闲物理块了,OS 会根据全局算法(如 LRU)在全内存范围内找一个“牺牲品”。这个“牺牲品”可能属于进程 B。
- 结果: 进程 B 的驻留集减小,进程 A 的驻留集增大或保持。
3. 该策略的优缺点
| 维度 | 评价 |
|---|---|
| 优点:极其灵活 | 系统可以根据整体内存的使用情况,动态地把资源调配给最需要的进程,内存利用率非常高。 |
| 优点:实现简单 | 操作系统只需维护一个全局的空闲块列表和全局页面置换算法,不需要为每个进程单独计算缺页率。 |
| 缺点:缺乏隔离 | 一个“表现差”的进程(疯狂产生缺页)可能会不断抢夺其他“表现好”进程的物理块,导致系统整体性能下降。 |
| 风险:系统抖动 | 如果大量进程同时疯狂抢夺物理块,会导致系统陷入频繁换入换出的死循环,即“抖动”。 |
4. 三种组合策略的“终极对比”
为了方便记忆,我们可以通过一个形象的比喻来区分这三种模式:
| 策略组合 | 形象比喻 | 适用场景 |
|---|---|---|
| 固定分配 + 局部置换 | “宿舍模式”:每个人分 4 张床,不管你带多少行李,多出来的只能叠在自己床上,不准占别人的。 | 嵌入式系统、实时系统。 |
| 可变分配 + 全局置换 | “公共休息室模式”:谁先来谁坐,没位子了就让最久没动的人(管他是谁)起来腾位子。 | 通用操作系统(Win/Linux)。 |
| 可变分配 + 局部置换 | “绩效工资模式”:根据你表现(缺页率)动态调配,表现好就多给你分个房,表现差就收回,但换人只能换你自己房里的。 | 高性能服务器、精密管理。 |
5. 总结
可变分配全局置换之所以流行,是因为它在处理“多任务并发”时效率最高。虽然它存在“进程间干扰”的风险,但现代内核通过优化的置换算法(如 Linux 的 kswapd 守护进程)很好地规避了严重抖动的问题。
3.可变分配局部配置
可变分配局部置换(Variable Allocation, Local Replacement) 是请求分页管理中一种非常高效且灵活的内存分配策略。
如果说“固定分配”是分给进程一个死工资,那么“可变分配”就是根据表现(缺页率)实时调整额度。
1. 核心定义
-
可变分配 (Variable Allocation):
操作系统为进程分配的物理块(驻留集)数量不是固定的。在进程运行期间,OS 会根据进程的实际需要,动态地增加或减少它占用的物理块。
-
局部置换 (Local Replacement):
当进程发生缺页时,只能从分配给该进程自己的物理块中选择一页换出。
2. 工作原理:基于“缺页率”的动态调节
这种策略的核心在于:如何科学地决定什么时候给进程增加块,什么时候减少块? 通常采用“缺页频率(PFF, Page Fault Frequency)”算法:
-
初始分配: 进程开始时,OS 分配一定数量的物理块。
-
缺页监控:
- 如果缺页率过高: 说明当前的驻留集(物理块)太小,装不下进程频繁访问的页面。此时,OS 会从全局空闲块中额外分配几个物理块给该进程,直到缺页率降低到可接受范围。
- 如果缺页率过低: 说明该进程占用的物理块太多了,有些页面可能很久没用了。此时,OS 会收回一部分物理块,分给其他急需内存的进程。
-
置换逻辑:
当进程 A 发生缺页,且 OS 认为暂时不该给它增加新块时,进程 A 必须在自己现有的物理块中选一个旧页换出,腾出位置给新页。
3. 该策略的优缺点
| 特点 | 说明 |
|---|---|
| 优点 1:性能最优 | 能够较好地平衡各个进程的需求。它能让每个进程的驻留集大小保持在“工作集”左右,极大地降低缺页率。 |
| 优点 2:防止抖动蔓延 | 虽然是可变的,但由于是“局部置换”,一个进程即便产生大量缺页,也只会在调整自己时影响系统,不会直接抢占其他进程已经分好的块。 |
| 缺点:实现复杂 | 操作系统需要时刻监控每一个进程的缺页率,并频繁地计算和调整物理块分配,增加了系统的管理开销。 |
4. 为什么它是“最先进”的组合?
在三大组合中,它的表现最出色:
- 固定分配局部置换: 太死板,容易造成浪费或频繁缺页。
- 可变分配全局置换: 虽然灵活,但容易导致“抢地盘”,一个进程抖动可能拖垮整个系统。
- 可变分配局部置换: 既有灵活性(可变),又有安全性(局部)。它能针对进程在不同生命周期(如初始化阶段 vs. 计算阶段)的不同需求,提供最合适的内存支持。
5. 总结:三大策略对比
| 策略 | 驻留集大小 | 置换范围 | 特点总结 |
|---|---|---|---|
| 固定局部 | 固定 | 只能换自己的 | 简单但缺乏灵活性。 |
| 可变全局 | 动态增加 | 可以换别人的 | 最容易实现,但进程间会相互干扰。 |
| 可变局部 | 动态增减 | 只能换自己的 | 最智能,能自动适应进程的工作集变化。 |
3.物理块调入算法
1. 平均分配算法 (Equal Allocation)
这是最简单、最直观的方法。系统将所有可用的物理块总数 $m$ 平均分配给当前运行的 $n$ 个进程。
- 计算公式: 每个进程分配到的物理块数 $b_i = \frac{m}{n}$
- 优点: 算法简单,易于实现,体现了“绝对公平”。
- 缺点: 极不合理。
- 大进程: 即使分配了平均份额,由于其代码和数据量大,依然会频繁发生“缺页中断”(抖动)。
- 小进程: 分配到的物理块可能远超其需求,导致内存资源的浪费。
2. 按比例分配算法 (Proportional Allocation)
这种方法根据进程的大小(逻辑空间大小)来按比例分配物理块。这是目前较为常用的一种策略。
- 逻辑: 进程越大,需要的内存通常越多。
- 计算公式: 设系统中所有进程的总页面数为 $S$,进程 $P_i$ 的页面数为 $s_i$,可用物理块总数为 $m$,则分配给 $P_i$ 的物理块数 $b_i$ 为:$$b_i = \frac{s_i}{S} \times m$$
- 优点: 相对合理,能够减少大型进程的缺页率。
- 缺点: 仅考虑了进程的静态大小,没有考虑到进程的实际运行特征(有的进程虽然大,但只有一小部分是活跃的)。
3. 优先权分配算法 (Priority Allocation)
在多道程序环境下,有些进程比其他进程更重要(如实时控制进程、交互式进程)。
- 分配方式: 通常采用混合分配:
- 一部分物理块按比例分配(保证基本运行)。
- 另一部分物理块根据优先权高低进行分配。
- 应用场景: 当系统急需处理某个重要任务时,会从低优先权进程中夺取物理块分配给高优先权进程。
- 优点: 能够保证核心业务或紧急任务的响应速度。
算法对比总结
| 算法 | 分配依据 | 公平性 | 效率/性能 |
|---|---|---|---|
| 平均分配 | 进程数量 | 数量上的公平 | 低(易导致大进程抖动) |
| 按比例分配 | 进程大小 | 资源占用上的公平 | 较高(目前主流做法) |
| 优先权分配 | 任务重要性 | 按需/按级分配 | 最高(保证关键任务) |
注意: 在实际应用中,无论采用哪种算法,分配给每个进程的物理块数都不能少于该指令集所需的最小物理块数,否则进程将无法正常运行。
4.调入的时机
1. 预调页策略 (Prepaging)
预调页是一种预防性的策略。系统根据空间局部性原理(如果一个页面被访问,其附近的页面也可能很快被访问),在程序运行前或运行中,“预测”未来可能需要的页面并提前调入。
- 时机: 通常发生在进程首次调入时。由程序员指定调入哪些页面,或者由操作系统根据历史运行轨迹自动选择。
- 优点: 能够显著减少进程开始运行时的缺页中断次数,提高效率。
- 缺点: 预测不一定准确。如果预调入的页面最后没有被使用,就会造成内存浪费和不必要的磁盘I/O。目前成功率通常仅在 $50%$ 左右。
2. 请求调页策略 (Demand Paging)
这是现代操作系统(如 Windows, Linux)中最常用的策略。它遵循“懒惰”原则:只有当进程在运行中发现所需的页面不在内存时,才由操作系统将其调入。
- 时机: 发生在进程运行过程中。当 CPU 访问某个逻辑地址,发现对应的页表项中“状态位”为 0(即不在内存)时,会触发一个缺页中断 (Page Fault),此时系统才去外存寻找该页。
- 优点: * 节省内存:只装入真正需要的页面。
- 启动快:进程无需等待所有页面装入即可开始执行。
- 缺点: 运行过程中会频繁发生缺页中断,每次中断都需要进行磁盘 I/O,这会产生一定的系统开销。
策略对比总结
| 特性 | 预调页策略 (Prepaging) | 请求调页策略 (Demand Paging) |
|---|---|---|
| 调入时机 | 进程运行前或运行中(预测性) | 缺页中断发生时(即时性) |
| 理论依据 | 空间局部性原理 | 运行时需求 |
| 主要用途 | 进程首次启动时的初始化 | 进程运行过程中的内存管理 |
| 性能影响 | 减少初期的缺页率 | 增加运行时的 I/O 开销 |
| 资源利用 | 可能浪费内存(如果预测失败) | 内存利用率高 |
补充:从何处调入?
无论何时调入,系统都需要从外存的“对换区”或“文件区”获取数据:
- 对换区 (Swap Space): 如果是曾经被换出的页面,通常从对换区快速调入。
- 文件区: 如果是程序首次运行或只读的代码段,通常直接从文件区调入。
5.从何处调入页面
在操作系统(请求分页存储管理)中,当发生缺页中断时,系统需要从外存(磁盘)将缺失的页面调入内存。调入的来源主要取决于磁盘空间的划分以及进程的运行状态。
通常情况下,磁盘被分为两个部分:文件区(存放普通文件、程序镜像)和对换区(专门用于存放从内存换出的页面)。
根据系统的不同实现和当前状态,调入页面的来源主要分为以下三种情况:
1. 系统拥有足够的对换区空间
在这种情况下,为了提高效率,所有的页面在进程运行前都会被复制到对换区。
- 来源: 所有的缺页请求都直接从对换区调入。
- 逻辑: 调出页面到对换区和从对换区调入页面的速度通常比从文件区(按文件系统结构查找)要快得多。
2. 系统缺少足够的对换区空间
由于对换区空间有限,系统必须采取“精打细算”的策略。
- 凡是不会被修改的页面: 直接从文件区调入。由于这些页面(如代码段)在运行过程中不会改变,当它们被换出时,无需写回磁盘,直接丢弃即可;下次需要时再从文件区重新读取。
- 凡是可能被修改的页面:(如数据段、堆栈、或已被修改过的页面),在换出时必须保存在对换区。下次缺页时,从对换区调入。
3. UNIX 方式(混合模式)
这是更接近现代操作系统(如 Linux)的实际处理方式。
- 未运行过的页面: 进程首次访问时,相关页面(代码和初始数据)一定位于文件区。
- 运行并换出的页面: * 如果页面被换出内存,它会被放进对换区。
- 下次再发生缺页时,从对换区调入。
- 特点: 这种方式兼顾了初始装载的便利和运行时的交换速度。
核心差异总结表
| 页面类型 | 物理来源 | 换出处理 |
|---|---|---|
| 可执行代码 (Text/Code) | 文件区 | 直接丢弃(不写回对换区) |
| 已初始化的数据 (Data) | 文件区 (初次) / 对换区 (之后) | 写回到对换区 |
| 堆栈/匿名内存 (Heap/Stack) | 对换区 (或分配零页) | 必须存放在对换区 |
知识点小贴士:
- 对换区 (Swap Space): 它的读写是不经过文件系统的复杂查找逻辑的,而是直接通过磁盘地址(扇区)进行连续读写,因此速度极快。
- 文件区 (File Area): 读写需要经过文件系统的管理(如目录项、索引节点等),随机访问开销相对较大。
6.如何调入页面
“调入页面”的底层实现过程,在计算机科学中被称为缺页中断处理 (Page Fault Handling)。
当进程运行过程中,如果访问的页面不在内存中,硬件(MMU)和操作系统(OS)会协同完成一系列复杂的步骤来“搬运”页面。
缺页中断处理的具体步骤
这是一个“发现缺失 -> 找位子 -> 搬东西 -> 改记录 -> 重新跑”的过程:
- 触发缺页中断: CPU 访问一个逻辑地址,硬件 MMU 在查页表时发现该页的“状态位”为 0(即不在内存中),立即产生一个缺页中断信号。
- 切换至内核态: CPU 暂停当前进程,保存现场(寄存器、程序计数器等),并将控制权交给操作系统的缺页中断处理程序。
- 合法性检查: OS 检查该虚拟地址是否合法。
- 如果访问了非法地址(如越界),则触发“段错误(Segmentation Fault)”,终止进程。
- 如果访问合法,则继续执行调页。
- 寻找空闲物理块: OS 在内存中寻找一个空的物理块(Frame)。
- 如果有空闲块: 直接使用。
- 如果内存已满: 启动页面置换算法(如 LRU、FIFO),选择一个“牺牲页”换出到磁盘,从而腾出一个空位。
- 启动磁盘 I/O: OS 查出该页在磁盘上的物理位置,启动磁盘驱动器,将页面内容读入选定的物理块中。
- 注:在读取期间,该进程处于阻塞状态,CPU 会切换去执行其他进程以提高利用率。
- 更新页表: 页面读取完成后,OS 修改页表项:
- 填入对应的物理块号。
- 将“状态位”改为 1(表示已在内存)。
- 重置其他位(如修改位、访问位)。
- 恢复执行: 恢复进程现场,重新执行刚才被中断的那条指令。由于此时页面已在内存中,指令将成功执行。
核心要素总结
| 阶段 | 参与者 | 动作核心 |
|---|---|---|
| 检测 | 硬件 (MMU) | 识别状态位为 0,发起 Trap(陷阱) |
| 决策 | 操作系统 | 检查合法性,决定是否置换页面 |
| 搬运 | 磁盘驱动器 | 将数据从辅存(对换区/文件区)搬入内存 |
| 收尾 | 操作系统 | 修正页表并重启指令 |
为什么调页会变慢?
从上述步骤可以看出,调页最耗时的部分是第 5 步(磁盘 I/O)。机械硬盘的访问速度比内存慢数千倍甚至数万倍。如果一个系统频繁发生缺页中断,CPU 就会一直等待磁盘,导致系统响应极慢,这种现象被称为“抖动(Thrashing)”。
为了更直观地理解缺页中断处理(页面调入)的完整过程,我为你绘制了这张流程图。它展示了从 CPU 发起访问到页面最终调入内存的每一个关键环节。
流程图要点解析:
- 硬件与软件的协作:
- MMU(硬件) 负责检测状态位,如果发现缺页,瞬间抛给操作系统。
- 操作系统(软件) 接手处理复杂的磁盘 I/O 和内存分配逻辑。
- 两个潜在的磁盘操作:
- 如果在第
H步发现内存已满,可能需要先执行换出(Swap Out),把旧页面写回磁盘。 - 然后必然执行换入(Swap In),把新页面读入内存。
- 如果在第
- 循环重试:
- 最后一步不是“继续执行”,而是“重新执行(Restart)”。这是因为之前指令因为缺页失败了,现在环境准备好了,需要让 CPU 重新尝试那次访问。