Linux 页面置换:教科书骗你说用的是 LRU

学操作系统的时候,页面置换算法那一章几乎总是从 FIFO 讲到 LRU,然后告诉你 LRU 太难实现,退而求其次用 Clock 算法。最后留下一个印象:大概就是个近似 LRU 吧,意思差不多就行。

但如果你去看 Linux 内核源码(mm/vmscan.c),会发现它根本没用任何教科书里写的那种 LRU。Linux 维护的不是一条 LRU 链表,而是两条——active list 和 inactive list,各自都是双向链表。新分配的页面先进 inactive list 的头部;被再次访问时通过设置 PG_referenced 位,晋升到 active list 的尾部。反过来,active list 尾部的页面在内存紧张时会被"降级"回 inactive list 的头部。而真正被换出的,是 inactive list 尾部的页面。

这个设计的核心直觉是:区分"热页面"和"温页面"。只被访问一次的页面(比如刚 mmap 读进去的大文件)停留在 inactive list,很快就会被扫到尾部换出,不会污染真正的热数据。只有被访问两次以上的页面才有资格进入 active list,享受更长的驻留时间。这比朴素 LRU 好在哪里?朴素 LRU 无法区分"刚读一次就不会再读"和"真正频繁在用"——它们都会因为最近访问过而被保护。而双链表结构天然给了一次性页面一个冷却窗口。

具体晋升逻辑在 mark_page_accessed() 函数里:页面第一次被访问,设置 PG_referenced;第二次被访问时如果 PG_referenced 已经置位,就把它从 inactive 升到 active。这使得一次性的顺序读(比如 cat 一个大文件)几乎不会冲击 active list,因为它们达不到"两次访问"的门槛。



#CS