内存管理
核心问题只有一个:如何在有限、异构、昂贵的物理存储资源之上,构造一个安全、可扩展、对程序友好的逻辑地址世界。
一、问题起点:物理内存的客观约束
1.1 分层存储器体系(Memory Hierarchy)
物理现实:
- 存储介质在容量、速度、成本上不可兼得
- 由此形成层级结构:寄存器 → Cache → 主存 → 外存
结论:
内存管理的本质,是在分层存储器体系之上进行数据放置与迁移决策。
1.2 存储管理器的职责
操作系统中负责内存的核心子系统,其职责不是“分配内存”,而是:
- 记录资源状态(使用 / 空闲)
- 进行空间隔离(Protection)
- 执行地址映射(Translation)
- 决定数据驻留位置(Placement & Replacement)
二、第一阶段:无存储器抽象(Bare Machine Model)
2.1 直接暴露物理地址的问题
在无抽象模型下:
- 程序直接使用物理地址
- 任意进程可读写全部内存
导致两个根本性缺陷:
- 无法隔离:安全性不可控
- 无法并发:多道程序设计几乎不可实现
2.2 静态重定位的历史方案
如 IBM 360:
- 通过编译期或加载期地址映射
- 逻辑地址 ≈ 偏移后的物理地址
关键认知:
这不是“抽象”,只是一次性地址修正。
三、核心抽象的诞生:地址空间(Address Space)
3.1 地址空间的本质定义
地址空间 = 一组逻辑地址的有序集合
关键不是“地址”,而是:
- 每个进程拥有独立命名空间
- 相同地址值 ≠ 相同物理位置
这与编程语言中的作用域 / 命名空间是同一类抽象。
3.2 抽象带来的两个核心能力
运行多个程序而互不干扰,必须解决:
- 保护(Protection):禁止非法访问
- 重定位(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
结论:
虚拟化并没有引入新问题,只是把“地址转换”堆叠了一层。
十六、常见内存错误的抽象视角
所有内存错误,本质都源于:
程序假设的地址语义,与系统维护的真实映射不一致。