1. 问题背景与核心挑战
在Linux内核的内存管理子系统中,MGLRU(Multi-Generational Least Recently Used)是一种近年来引入的页面回收算法改进方案。它通过引入多代LRU链表来更精确地追踪页面活跃度,从而提升页面回收效率。但在实际部署过程中,我们发现了一个影响性能的关键问题:当MGLRU与readahead机制交互时,某些情况下会触发非最优的页面激活行为。
具体表现为:当用户进程通过mmap方式访问文件时,内核的readahead机制会预读后续可能需要的文件内容到内存页面(folios)。按照传统LRU的设计,这些预读的folios会立即被标记为活跃状态并加入活跃LRU链表。但在MGLRU架构下,这种过早激活会导致两个问题:
-
代际年龄失真:预读的folios可能实际上并未被立即使用,但因其被过早标记为活跃,导致MGLRU无法准确判断其真实热度。
-
缓存污染风险:大量可能根本不会被访问的预读folios占据活跃链表,挤占真正活跃页面的空间。
2. 解决方案设计思路
我们的核心改进思路是:将readahead folios的激活时机延迟到实际发生page fault的时刻。具体来说:
-
初始状态处理:当readahead机制预读folios时,这些folios仅被加入inactive链表,不立即提升其代际年龄。
-
访问时激活:只有当进程实际通过page fault访问到这些folios时,才将其转移到活跃链表并更新代际信息。
这种设计带来三个关键优势:
-
准确性提升:代际年龄现在真实反映页面的实际使用情况,而非预测使用情况。
-
资源利用率优化:避免缓存被可能无用的预读内容污染,提高真正活跃页面的驻留率。
-
行为一致性:与MGLRU的设计哲学保持一致——让回收决策基于实际访问模式而非启发式预测。
3. 关键技术实现细节
3.1 修改点定位
主要改动集中在以下内核路径:
- readahead机制(mm/readahead.c):
- 修改
__do_page_cache_readahead()函数 - 新增
mglru_deferred_activate标志控制
- 修改