物理帧装不下工作集时,缺页率立刻上来;但降到【每页第一次访问】就降不动了——那 8 次叫强制缺页,谁也免不了。 Swap 把【杀进程】换成【变慢】,但换多了就是抖动。
5.14 讲了页可以在进程之间共享,这一节讲页可以被挪到磁盘上再搬回来——这是虚拟内存最容易被误解的一个用途。
一、Swap 是什么
在被换出的页上,页表项的 P 位改成 0,原来的物理页被腾出来给别人用;内容写到磁盘上的交换区。 下次访问这一页:又触发缺页,内核从交换区读回来,填好页表。 所以 Swap 不是【扩大内存】,是【把内存里最不常用的部分暂存到磁盘】。 它的代价是【访问那一页从纳秒变成毫秒】——慢了六个数量级。
二、帧数决定缺页率
八个页、冷热不均地访问一千次:帧 1 缺页 807 次、帧 2 缺 593 次、帧 4 缺 289 次、帧 8 缺 8 次。 帧越少缺得越多,但降到 8 次就降不动了。 因为那 8 次是【每个页第一次访问】——叫强制缺页,谁也免不了。 能省的只有【重复访问】那一部分,而重复访问要靠帧装得下工作集。 所以【加大内存】能解决的是缺页率里【可省的那一部分】,不是全部。
三、有 Swap 和没 Swap
稍微超一点:有 Swap 就换出几页,活还能干完;没 Swap 就只能回收缓存。 超太多:没 Swap 就是 OOM Killer(快死);有 Swap 就是反复换入换出(慢死)。 所以【有 Swap 更安全】只在【轻度超额】时成立。重度超额时,有 Swap 往往更难受——磁盘一直转,什么请求都要等几秒。 这就是【抖动(thrashing)】:系统的全部时间都花在换页上,不干正事。
四、挑哪一页换出去
FIFO:换最早进来的——问题是最早的可能还在用。 LRU:换最久没访问的——听起来对,但要记每个页的访问时间,太贵。 时钟(clock):LRU 的近似——给每页一个【用过】标志,指针转一圈,遇到用过就清零、遇到没用过就换出去。 Linux 用的是近似 LRU:它不追求挑得最准,只求挑得快。 这又是那个熟悉的取舍:精确的代价往往比不精确的损失还大。
五、动手 Demo:Swap——内存不够时,把不用的页挪出去
这一节用一个 LRU 模拟器,把【物理帧数】当成变量,看缺页率怎么变,以及为什么它降不到零。
def lru(seq, frames):
mem, faults = [], 0
for p in seq:
if p in mem:
mem.remove(p)
mem.append(p)
else:
faults += 1
if len(mem) >= frames:
mem.pop(0)
mem.append(p)
return faults
for f in (1, 2, 4, 8):
print(f, "帧", lru(seq, f), "次缺页")
实跑输出:
--------------------------------------------------------------------
Swap——内存不够时,把不用的页挪出去
--------------------------------------------------------------------
① 1000 次访问,8 个页,冷热不均(前两个页占一半多)
物理帧数 缺页次数 缺页率
---------------------------------
1 807 次 80.7%
2 593 次 59.3%
4 289 次 28.9%
8 8 次 0.8%
**帧越少缺页越多,但降到 8 次就降不动了**——
**因为那 8 次是【每个页第一次访问】,谁也免不了(叫强制缺页)。**
**能省的只有【重复访问】那一部分,而重复访问要靠帧装得下工作集。**
② 有 Swap 和没 Swap,结局不一样
情况 内核怎么办 用户感觉到什么
------------------------------------------------------------------
有 Swap,稍微超了 换出最久没用的页 变慢,但活干完了
没 Swap,稍微超了 只能回收缓存 磁盘 IO 变多
没 Swap,超太多 OOM Killer 进程直接没了
有 Swap,超太多 换出太多,反复换入换出 **系统假死**
**最后一行最坑**:**内存严重不够时,有 Swap 反而不如没有。**
**因为它在【慢死】和【快死】之间选了慢死。**
③ 挑哪一页换出去
算法 挑谁 问题
----------------------------------------------------------
FIFO 最早进来的 可能换掉正在用的
LRU 最久没用过的 要记访问时间,贵
时钟(clock) LRU 的近似 精确度差一点,便宜很多
**Linux 用的是近似 LRU**——它不追求挑得最准,只求挑得快。
帧 1 缺 807 次、帧 8 缺 8 次——差两个数量级。 但帧 8 那 8 次是【每个页第一次访问】,再怎么加帧也降不到 0。 所以【内存不够】的判据不是【有没有缺页】,是【缺页率是不是一直很高】。 注意帧 4 就已经只有 28.9% 了——这说明【多给一点内存】的收益是递减的,不是线性的。 这也解释了【加到 32 GB 之后为什么没感觉】:因为可省的那部分早就省完了,剩下的全是强制缺页。
📋 Linux 上看有没有在换页:vmstat 1 的 si(换入)与 so(换出)两列——长期非零就是在换页,系统已经在拿磁盘当内存用。
30 秒实验
vmstat 1 5,看 si 与 so 两列是不是 0。free -h,看 Swap 那一行的 used 是多少。cat /proc/sys/vm/swappiness,看内核有多愿意换出(默认 60,数据库常调低)。
常见误区
- 【Swap 是用来扩大内存的】。它是把不常用的页暂存到磁盘,访问那些页会慢六个数量级。
- 【有 Swap 就比没 Swap 安全】。只在轻度超额时成立;重度超额时抖动比 OOM 更难受。
- 【缺页率高就是内存不够】。还有强制缺页这一部分,加内存也降不掉。
自测三问(点开对答案)
1. Swap 换出一页时,页表项怎么变? —— **P 位改成 0**,物理页腾出来;**下次访问触发缺页,从交换区读回来。** 2. 为什么缺页率降不到零? —— **每个页第一次访问必然缺一次(强制缺页)**,这部分加内存也消不掉。 3. 什么情况下有 Swap 反而更糟? —— **内存严重不足时**:**反复换入换出(抖动)**,**系统全部时间花在换页上,用户感觉假死。**- ☐ 5.16 多级页表
相关笔记
- 内存不足 — 三步顺序里的第二步
- 请求分页 — 缺页的另一半原因
- 页面缓存 — 换入换出的对手
- 🐧 Linux是怎样工作的 · 索引