Skip to content

虚拟内存与页面置换

什么是虚拟内存

每个进程看到的是独立、连续的虚拟地址空间。CPU 的 MMU(内存管理单元)依据页表把虚拟页映射到物理页框;映射不存在或权限不符时触发异常,由内核处理。

进程内存隔离如何实现

每个进程有自己的页表。即使进程 A 与进程 B 使用相同虚拟地址,它们也会经各自页表映射到不同的物理页;若页表中没有映射或权限不允许,CPU 会触发异常,内核通常终止违规进程(如段错误)。进程切换时,CPU 同时切换当前使用的页表,因此用户态进程不能直接访问其他进程的私有内存。

页表还包含读、写、执行以及用户态/内核态等权限位。内核以更高特权级管理页表和物理内存;共享内存、mmap 和共享库则是内核显式让多个进程映射同一物理页的例外,此时需要额外同步来保护共享数据。

虚拟内存带来:

  • 隔离与保护:不同进程默认无法任意读写彼此内存;页面可标记为可读、可写、可执行;
  • 地址空间抽象:程序不必知道物理内存位置,方便装载、共享库与地址随机化;
  • 按需调页:只在真正访问时把页面装入物理内存;
  • 文件映射与共享:文件可映射到内存,多个进程也可映射同一只读代码页或共享内存页;
  • 超额使用的有限支持:不活跃匿名页可换出到 swap,或可丢弃后从文件重新读取;但频繁换页会造成严重抖动,不能把磁盘当作等价内存。

缺页与交换

访问未驻留页面会产生缺页异常(page fault):内核检查地址是否合法,若合法则从文件或交换区载入页,更新页表后恢复指令;若非法则向进程报告错误(例如段错误)。

当物理内存紧张,系统会回收页面:干净的文件页可直接丢弃后按需重读;脏页需回写;匿名页可能写入交换空间。持续的高缺页率导致系统大部分时间在调页,称为抖动(thrashing)

常见页面置换算法

算法核心思想特点
OPT淘汰未来最长时间不会被访问的页理论最优,需要预知未来,不能实际实现,常作基准
FIFO淘汰最早进入内存的页简单,但可能出现 Belady 异常
LRU淘汰最近最久未使用的页利用时间局部性;严格实现成本高
LFU淘汰访问次数最少的页利用频率局部性;需处理“历史热点”和计数老化
Clock / Second Chance环形扫描访问位,访问位为 1 则清零并跳过,为 0 则淘汰近似 LRU、实现成本低,操作系统常见

现代系统通常采用 LRU 的近似与多队列/工作集等策略,而非直接实现教材中的单一算法。

C/C++ 常见内存区域(概念模型)

不同 ABI、编译器和操作系统的具体布局不同,以下是理解生命周期的常见划分:

区域内容生命周期/管理
代码段(text)可执行指令,常为只读可共享程序映像存在期间
只读数据字符串字面量、只读常量等程序映像存在期间
数据段(data)已初始化的全局/静态对象静态存储期
BSS零初始化的全局/静态对象静态存储期
堆(heap)动态分配对象及分配器管理区域显式释放或 RAII 管理
栈(stack)调用帧、自动局部对象、部分参数作用域/函数返回时自动销毁

“栈上/堆上”描述的是常见实现与分配方式,不应代替 C++ 的正式概念:自动、动态、静态和线程存储期。

堆与栈

  • 通常按调用帧后进先出管理,分配/回收快但容量有限;深递归或大局部数组可能栈溢出。
  • 由分配器管理,适合运行期大小或跨作用域生命周期的对象;分配成本和碎片问题更复杂。
  • C++ 中应优先让对象由值、容器和智能指针按 RAII 管理,而不是把“手动 new/delete”当作默认方式。

使用 Markdown 与 VitePress 构建