当前位置: 首页 > news >正文

操作系统 内存(5)虚拟内存机制

目标:

1.理解按需调页、页面置换、缺页中断
2.为什么虚拟内存能让程序“超出”物理内存?LRU、Clock 算法如何实现?

非常好 👍,你现在到了操作系统内存管理中最“灵魂”的部分——虚拟内存机制(Virtual Memory)
这是理解现代计算机“如何让有限的物理内存运行看似无限程序”的关键。
我们慢慢展开讲,像之前那样:从问题出发 → 原理 → 机制 → 细节(LRU、Clock)


🧩 一、问题起点:为什么需要“虚拟内存”?

在分页机制(paging)出现后,我们已经能做到:

  • 把程序分成页(Page),内存分成页框(Frame),按页加载;
  • 逻辑地址 → 页号 + 页内偏移 → 页表映射到物理地址。

这很好,但仍然有两个重大限制

  1. 程序必须完全在内存中才能运行。
    这意味着:

    • 程序大 → 必须占大量内存;
    • 内存小 → 程序无法运行;
    • 多程序并行时,很快就会内存不够。
  2. 用户看见的内存 = 真实物理内存。
    你在代码里开了一个 4GB 数组,如果物理内存才 2GB,那直接崩溃。


🧠 二、核心思想:让“假”内存看起来像“真”的

虚拟内存的关键就是:

程序以为自己拥有一个完整、连续的大内存空间,
实际上只是部分装进了物理内存,其余部分临时放在磁盘上(swap space)。

我们用一张图感受一下这个“虚假但有效的幻觉”:

虚拟地址空间(程序视角)       物理内存 + 磁盘(系统视角)
┌──────────────────┐       ┌───────────┬─────────────┐
│ Code Segment     │ --->  │ Frame 1   │             │
│ Data Segment     │ --->  │ Frame 2   │             │
│ Stack Segment    │ --->  │ Frame 3   │             │
│ Heap Segment     │ --->  │    ...     │  Disk Swap  │
└──────────────────┘       └───────────┴─────────────┘

当程序访问某个还未加载进内存的页时,就会触发:

缺页中断(Page Fault)
→ 操作系统从磁盘把这页调入内存(按需调页)。
→ 如果内存满了,就得换出一个旧的页(页面置换)。


很好👌,那我们现在进入虚拟内存中最精妙、也最体现“操作系统与硬件协作”的部分——

🧠 缺页中断(Page Fault)全过程

从 CPU 执行一条指令开始,到缺页、调页、恢复执行为止。


(1)问题起点:CPU 访问到一个“不在内存中的页”

想象一个场景:

程序执行到一句代码:

int x = array[10000];

在逻辑上,它只是在访问自己的内存空间,但在底层,这一步会发生:

  1. CPU 根据虚拟地址 VA 去查页表;

  2. 页表项(PTE, Page Table Entry)告诉 CPU:

    • 该页是否在内存中;
    • 在内存中时,对应的物理页框号;
    • 是否可写、是否在用户态可访问等权限位。

现在如果 PTE 中的“存在位(Present bit)”是 0 ——
意味着这个页还没被加载到内存中。

于是 CPU 就会喊出一句话:

“Page Fault!”


(2)缺页中断触发机制(硬件)

CPU 一旦发现页不存在,就会:

  1. 暂停当前指令执行
  2. 触发缺页中断(Page Fault Exception)
  3. 硬件自动保存现场(例如程序计数器、寄存器状态);
  4. 跳转到操作系统内核的中断处理程序

⚙️ 注意:这时 CPU 并不知道怎么处理,它只是“告诉 OS:你得来帮我拿内存”。


(3)操作系统接管:缺页处理流程(软件)

现在内核登场。

它会按以下步骤工作:

CPU 发现页不存在↓
触发 Page Fault 中断↓
操作系统接管↓
根据页表找到对应的虚拟页↓
判断缺页原因:├─ 是否非法访问(越界、权限错误)└─ 是否只是“该页还未加载”↓
如果合法但未加载:→ 选一个物理页框→ 若内存满:执行页面置换算法(LRU / Clock)→ 从磁盘中调入该页内容(I/O操作)→ 更新页表(将该页标记为存在)↓
恢复 CPU 上下文↓
重新执行被中断的那条指令

整个过程大致如下图:

┌──────────────────────────────┐
│ 程序访问虚拟地址 VA          │
│ 页表查找:P=0 → 缺页中断     │
└───────┬──────────────────────┘│▼
┌──────────────┐
│ OS 中断处理程序 │
└──────────────┘│▼[检查缺页合法性]│▼[如合法 → 选页框]│▼[如需置换 → 写回旧页]│▼[从磁盘调入新页]│▼[更新页表、标记P=1]│▼[恢复现场,重新执行指令]

(4)一次缺页的代价

这个过程虽然优雅,但代价很大:

  • CPU 访问内存:纳秒级(~100ns);
  • 从磁盘读取页面:毫秒级(~10ms)。

也就是说:

一次缺页中断的时间成本 = 普通内存访问的十万倍!

所以系统必须:

  • 尽量减少缺页率;
  • 用良好的局部性(locality)和算法;
  • 并在硬件上加速(比如 TLB 缓存最近访问的页表)。

(5)缺页的类型

不是所有的“页不在内存”都一样,OS 会区分几种情况:

类型含义处理方式
初次调页(Demand Zero)页第一次被访问,还未分配实际物理页分配一个空白页,初始化为 0
文件页缺页(File-backed)页对应程序文件(代码段、数据段)从可执行文件中读入
匿名页缺页(Anonymous)动态内存、栈等分配空页或从 swap 区加载
写时复制页(Copy-on-Write)多进程共享页,写入时触发分配新页并复制数据

这让虚拟内存不只是“假内存”,
而是一整套“高效调度物理资源的机制”。


⚙️ 三、机制细节:按需调页 + 页面置换

🧠 TLB(Translation Lookaside Buffer)与地址转换加速


(1)为什么要有 TLB?

前面我们说过:
CPU 执行一条内存访问指令时,要把虚拟地址(VA)→物理地址(PA)
这个映射靠的是页表(Page Table)

但是——
假如每次访问内存都要查一遍页表,那就太慢了。
因为页表本身存在于内存中

让我们算一下:

  • 访问一次数据 = 查页表一次 + 访问数据一次
  • 每次访问都多一次内存读操作

相当于:

原本 1 次内存访问的代价 → 变成 2 次(甚至更多)

那程序的性能几乎直接砍半。

于是——
硬件工程师灵机一动:
能不能把最近常用的地址映射缓存起来

这就诞生了:
👉 TLB(Translation Lookaside Buffer)翻译后备缓冲器


(2)TLB 是什么?

TLB 是 CPU 内部的一个高速缓存(在芯片上),
专门用来存放最近使用过的虚拟页号 → 物理页号的映射。

项目页表TLB
存放位置主存(内存)CPU 内部
存取速度慢(~100ns)快(~1ns)
存储内容所有页的映射最近访问的部分映射
大小几千~上百万项几十~几百项

所以,TLB 就像是“页表的缓存”。


(3)CPU 访问内存的过程(有了 TLB 之后)

我们重新看一下 CPU 访问一条地址的完整过程:

虚拟地址(VA)↓
查 TLB↓
┌──────────────┐
│ 命中(hit) │
│ → 得到物理页号 │
└──────────────┘↓
拼接页内偏移,得到物理地址↓
访问内存成功

如果没命中(miss):

虚拟地址(VA)↓
查 TLB↓
┌──────────────┐
│ 未命中(miss)│
│ → 查页表(内存)│
│ → 得到物理页号 │
│ → 更新 TLB 缓存 │
└──────────────┘↓
访问内存成功

(4)TLB 命中率的重要性

TLB 通常很小(几十到几百项),
但局部性原理帮了大忙:

程序通常在一小段地址空间中反复访问数据。

因此,TLB 的命中率往往能达到 95%~99%
这意味着——绝大多数访问根本不需要查页表。

假设:

  • 命中开销 1ns;
  • 未命中需要查页表+访问 200ns;
  • 命中率 99%。

平均访问时间:

Tavg = 1ns * 99% + 200ns * 1% ≈ 2ns

比原来的 200ns 快了 100 倍以上。


(5)TLB 与多级页表的配合

现代系统通常使用多级页表(如 4 级页表)。

如果没有 TLB:

  • 每次地址转换要查 4 次内存(每一级页表都要访问一次);
  • 再加上实际访问数据 1 次;
    一次访问 = 5 次内存读

有了 TLB:

  • 命中时只查一次;
  • 极大减少开销。

所以 TLB 是“虚拟内存性能的救命稻草”。


(6)TLB 的刷新与上下文切换问题

问题来了:
TLB 里存的是“虚拟页号 → 物理页号”,
但不同进程的虚拟页号会重复。

如果两个进程都运行,TLB 不清空会出错。

于是有两种机制:

  1. 上下文切换时清空 TLB(简单但慢);
  2. 带 ASID(Address Space ID)标识的 TLB
    每个进程有唯一 ID,TLB 项带上它,
    这样不用清空,也能区分不同进程的地址空间。

现代 CPU(如 x86-64, ARM)基本都支持 ASID 或 PCID 技术。


(7)TLB 的层次结构

和缓存一样,TLB 也分层:

类型位置特点
L1 TLB每个核心独立极快(十几项)
L2 TLB共享或更大稍慢(几百项)

访问顺序通常是:

L1 TLB → (miss) → L2 TLB → (miss) → 查页表

有时你可能听到术语:

“TLB shootdown”
指多核 CPU 中,某个核心修改页表时,要让其他核的 TLB 失效(同步更新)。


页面置换(Page Replacement)

如果内存已满,又来了一个新的页:

  • 系统必须选择一个旧页换出(写回磁盘);
  • 然后把新页装进来;
  • 更新页表映射关系。

这就是 页面置换算法 的用武之地。

我们看两个最常考、也最有代表性的:

🧮 (1) LRU(Least Recently Used)

  • 思想:最近没被用的页,未来也可能不会被用。

  • 实现方式

    • 用时间戳记录每个页最近访问时间;
    • 缺页时,选择最久未被访问的页置换。
  • 优点:近似最优;

  • 缺点:时间戳维护成本高(硬件或额外表支持)。

举个例子:

假设内存可容 3 页,访问序列为:

7, 0, 1, 2, 0, 3, 0, 4

模拟 LRU:

7 -> [7]
0 -> [7,0]
1 -> [7,0,1]
2 -> 缺页 -> 替换最久未用的 7 -> [0,1,2]
0 -> 已存在
3 -> 替换最久未用的 1 -> [0,2,3]
0 -> 已存在
4 -> 替换最久未用的 2 -> [0,3,4]

🕰️ (2) Clock(近似 LRU)

  • 思想:维护一个环形队列和一个“使用位”。

  • 过程

    1. 每页有一个 use_bit(访问位)。

    2. 当访问页时,置 use_bit = 1。

    3. 置换时,从“指针”开始扫描:

      • 如果 use_bit = 0,换出;
      • 如果 use_bit = 1,设为 0,继续走一圈。

这就像一个“时钟的秒针”,扫过一圈,找出“最冷门”的页。
它比 LRU 更高效(无需时间戳),又能逼近效果。


💡 四、虚拟内存的关键意义

  1. 让程序“超越”物理内存运行
    程序只需局部驻留内存即可运行,系统可同时支持多个大型程序。

  2. 进程隔离与安全性
    每个进程都有独立的虚拟地址空间,不会互相访问越界。

  3. 内存利用最大化
    不活跃的页可换出磁盘,内存专注服务活跃部分。


🧩 五、总结思维图

虚拟内存机制
│
├── 目的:让程序看似有大内存
│
├── 核心机制:
│     ├─ 按需调页(只加载需要的页)
│     ├─ 缺页中断(访问未加载页时触发)
│     └─ 页面置换(内存满时替换页)
│
├── 常用算法:
│     ├─ LRU(最久未使用)
│     └─ Clock(近似 LRU)
│
└── 结果:├─ 支持超大程序├─ 提高并发性└─ 隔离安全

http://www.dtcms.com/a/589614.html

相关文章:

  • 郑州网站建设专业乐云seowordpress user role
  • JavaScript 的 Web APIs 入门到实战全总结(day7):从数据处理到交互落地的全链路实战(附实战案例代码)
  • 分类型网站建设付费推广外包
  • 17_FastMCP 2.x 中文文档之FastMCP服务端高级功能:LLM采样详解
  • 集团网站建设制作费用百度公司是国企还是私企
  • Go Channel 深度指南:规范、避坑与开源实践
  • Postman 脚本控制特定请求的执行流程(跳过执行)
  • Kubernetes Deployment 控制器
  • 网络体系结构-物理层
  • 色彩搭配 网站无障碍网站建设方案
  • 网站建设制作公一般做个网站多少做网站多少钱
  • 商业网站建站目的官网建站系统
  • HCCDE-GaussDB相关计算题
  • 从SOMEIP看SOA,汽车电子电器架构的转变
  • 免费自己制作logo的网站wordpress百度百科
  • asp制作网站教程猎头公司网站素材
  • Java--JVM
  • 英语学习——单词篇(第十七天)
  • 福州做网站wordpress修改footer
  • 顺序表vector--------练习题9题解
  • 深入浅出:低噪声放大器(LNA)与USB芯片——无线与有线通信的基石
  • C++线程操作
  • 培训网站网站建设上海 网站建设google
  • OpenCV 第10课 图像处理—阈值处理
  • 力扣刷题-借助哈希完成一次遍历
  • 网络图标误报?电脑显示无网却能上网的快速修复法
  • 二七区做网站动画设计培训机构
  • 做网站九州科技哈尔滨网络公司定制开发
  • 链动2+1模式、AI智能名片与S2B2C商城小程序:破解直播电商流量转化困局的创新路径
  • 建设网站基本思路系统页面模板