iBoxHub技术日志

怀念我们的昨天,憧憬我们的明天,珍惜我们的今天

内存管理

内存管理

核心问题只有一个:如何在有限、异构、昂贵的物理存储资源之上,构造一个安全、可扩展、对程序友好的逻辑地址世界。


一、问题起点:物理内存的客观约束

1.1 分层存储器体系(Memory Hierarchy)

物理现实:

  • 存储介质在容量、速度、成本上不可兼得
  • 由此形成层级结构:寄存器 → Cache → 主存 → 外存

结论

内存管理的本质,是在分层存储器体系之上进行数据放置与迁移决策

1.2 存储管理器的职责

操作系统中负责内存的核心子系统,其职责不是“分配内存”,而是:

  • 记录资源状态(使用 / 空闲)
  • 进行空间隔离(Protection)
  • 执行地址映射(Translation)
  • 决定数据驻留位置(Placement & Replacement)

二、第一阶段:无存储器抽象(Bare Machine Model)

2.1 直接暴露物理地址的问题

在无抽象模型下:

  • 程序直接使用物理地址
  • 任意进程可读写全部内存

导致两个根本性缺陷:

  1. 无法隔离:安全性不可控
  2. 无法并发:多道程序设计几乎不可实现

2.2 静态重定位的历史方案

如 IBM 360:

  • 通过编译期或加载期地址映射
  • 逻辑地址 ≈ 偏移后的物理地址

关键认知

这不是“抽象”,只是一次性地址修正。


三、核心抽象的诞生:地址空间(Address Space)

3.1 地址空间的本质定义

地址空间 = 一组逻辑地址的有序集合

关键不是“地址”,而是:

  • 每个进程拥有独立命名空间
  • 相同地址值 ≠ 相同物理位置

这与编程语言中的作用域 / 命名空间是同一类抽象。

3.2 抽象带来的两个核心能力

运行多个程序而互不干扰,必须解决:

  1. 保护(Protection):禁止非法访问
  2. 重定位(Relocation):地址可移动

3.3 基址 / 界限寄存器模型

  • 基址寄存器:映射起点
  • 界限寄存器:合法范围

认知层结论

这是最小可用的地址空间实现,但扩展性极差。


四、资源压力的应对:交换与连续内存管理

4.1 局部性原理(Locality)

程序行为呈现:

  • 时间局部性
  • 空间局部性

这是虚拟内存一切机制的理论基础

4.2 交换(Swapping)的思想

  • 整个进程作为单位换入 / 换出
  • 用磁盘换取并发度

问题:

  • 粗粒度
  • 高开销
  • 容易产生碎片

4.3 连续内存的空闲管理

两类本质模型

  • 位图(Bitmap):空间换时间
  • 链表(Free List):时间换空间

所有“首次 / 最佳 / 最差 / 快速适配”算法,本质上都是:

在碎片率与搜索成本之间取舍


五、质变点:虚拟内存(Virtual Memory)

5.1 抽象升级

地址空间 ≠ 物理内存大小

虚拟内存将地址空间切分为页(Page)

  • 逻辑上连续
  • 物理上离散

5.2 页、页框与映射

  • 虚拟页(VP)
  • 物理页框(PP)
  • 页表(Page Table)描述映射关系

访问不存在映射的页 → 缺页中断


六、地址转换体系:MMU + 页表 + TLB

6.1 MMU 的角色定位

MMU 不是“操作系统组件”,而是:

硬件执行抽象的载体

职责:

  • 虚拟地址 → 物理地址
  • 权限校验

6.2 页表的逻辑模型

每个页表项(PTE)至少包含:

  • 有效位
  • 物理页号 / 磁盘地址
  • 权限位(R/W/X)
  • 状态位(访问 / 修改)

6.3 TLB:缓存加速抽象

TLB 的本质:

地址转换结果的高速缓存

它将一次可能需要多次内存访问的操作,压缩为一次硬件并行匹配。


七、规模问题:大地址空间的页表设计

7.1 多级页表

  • 按需创建
  • 空间换时间

7.2 倒排页表

  • 以物理页为中心
  • 必须依赖 TLB

统一认知

页表设计的本质是:稀疏映射的压缩表达


八、多进程语义:共享、隔离与保护

8.1 虚拟内存的进程视角

  • 地址相同 ≠ 数据相同
  • 物理页可被多个地址空间映射

8.2 共享的工程价值

  • 共享库
  • 只读代码
  • 写时复制(Copy-on-Write)

8.3 内存保护模型

权限控制并不作用于“物理内存”,而是:

作用于地址空间到物理页的映射关系


九、缓存淘汰的统一模型:页面置换算法

9.1 统一抽象

页面置换 ≈ 缓存淘汰

9.2 算法谱系

  • OPT:理论上界
  • FIFO:时间近似
  • LRU:访问历史近似
  • Clock / Aging:工程折中
  • Working Set:程序语义感知

核心权衡

  • 精确度
  • 维护成本
  • 硬件支持

十、系统稳定性问题:颠簸与负载控制

  • 缺页率(PFF)
  • 局部 / 全局分配
  • 负载削减

目标只有一个:

防止系统进入自激振荡(Thrashing)


十一、分段、分页与历史融合

11.1 分段的动机

  • 程序语义可见
  • 天然支持共享与保护

11.2 分页的动机

  • 支持虚拟内存
  • 消除外部碎片

11.3 段页式模型

  • MULTICS
  • x86(GDT / LDT + Paging)

结论

分段解决“语义”,分页解决“规模”。


十二、进程内存布局与系统调用接口

  • Code / Data / BSS
  • Heap(brk / sbrk / mmap)
  • Stack
  • Shared Library
  • Memory-mapped Files

现代内存管理已高度依赖 mmap 作为统一抽象接口


十三、用户态分配器:再一次抽象

13.1 malloc 的真实职责

malloc 并不管理“内存”,而是:

管理一段虚拟地址区间的子分配

13.2 分配器的核心设计问题

  • 空闲块组织
  • 放置策略
  • 分割与合并
  • 并发优化(Thread Cache)

所有实现(ptmalloc / jemalloc / tcmalloc)都是在这组问题上的不同取舍。


十四、异常与中断:抽象失效的处理路径

14.1 页异常类型

  • 未映射
  • 不在内存
  • 写保护
  • 权限违规

14.2 缺页中断处理流程

  • 保存现场
  • 判定合法性
  • 选择牺牲页
  • IO 交换
  • 更新映射
  • 恢复执行

十五、虚拟化场景下的内存管理

  • GVA → GPA → HPA
  • Shadow Page Table
  • EPT / NPT

结论

虚拟化并没有引入新问题,只是把“地址转换”堆叠了一层。


十六、常见内存错误的抽象视角

所有内存错误,本质都源于:

程序假设的地址语义,与系统维护的真实映射不一致