LabHub
学习 学习路径 课程

操作系统

请求分页与抖动 — 内存不够时会发生什么

在 LabHub 中继续学习

一句话总结

只在需要时载入页面,可以运行比物理内存更大的程序;但一次缺页的成本比内存访问高数万倍,缺页率稍有上升就可能拖垮系统。

流程图: 缺页异常 · 那条指令 · 8,200ns · 40 倍

为什么需要它

程序不必整体装入内存。错误处理代码等部分很少执行,大数组也常只使用一小部分。按需分页只在使用时载入页面,既能同时运行更多进程,也能缩短程序启动时间。

工作原理

页表项含有效位。访问无效页会触发缺页异常,处理顺序如下:

  1. 触发陷阱,进入内核。
  2. 判断是非法地址还是尚未载入的页面。
  3. 寻找空闲页框;若没有,则选择牺牲页换出。若该页已修改(dirty),先写入磁盘。
  4. 从磁盘读取页面并装入页框。
  5. 更新页表,从导致异常的那条指令重新执行。

成本十分惊人。假设内存访问为 200ns,缺页处理为 8ms,缺页概率为 p,则有效访问时间是 (1−p)×200 + p×8,000,000。

页面置换算法也各有特点。对引用串 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 分配 3 个页框时,FIFO 缺页 15 次,LRU 12 次,理论最优 OPT 9 次。FIFO 甚至存在增加页框后缺页反而增多的Belady 异常。实际系统不用纯 LRU,而常用基于引用位的**时钟(second-chance)**算法,以更低成本取得接近 LRU 的效果。

抖动(thrashing) 是页框不足导致缺页激增、CPU 因此空闲,而操作系统误以为负载不高又加入更多进程,使情况继续恶化的循环。应跟踪工作集(近期实际引用的页面集合)并保证足够页框,或直接监控缺页率调整分配。

实际工作中的表现

容器设定内存上限后可能有两种结局。关闭交换时,OOM Killer 会杀死进程,虽突然但原因明确。启用交换时则可能开始抖动:进程还活着,响应却慢几十倍,CPU 使用率低而 iowait 飙升。因此常说被杀反而更容易诊断。Kubernetes 长期要求关闭交换,也源于这种不可预测性。

后续测验将确认什么

确认你能亲自计算缺页成本,并解释抖动为何是会自我强化的恶性循环。