浅谈Linux内核的LRU算法作用和原理
在 Linux 内核中,LRU (Least Recently Used,最近最少使用)算法是内存管理的核心策略之一,它旨在高效地管理物理内存(RAM)资源,优化系统性能。其核心是识别并优先回收那些长时间未被访问的内存页,以解决内存不足时的页面置换问题。
一、LRU的核心作用与解决的问题
问题根源: 物理内存有限,远超内存容量的数据会被缓存到磁盘(Swap空间)。当内存不足时,必须将部分页换出到磁盘。
关键痛点: 换出哪些页?
错误选择(如换出频繁访问的页)会引发高频磁盘I/O,导致性能急剧下降("抖动")。
LRU的作用: 最大化保留热数据(近期访问过的页),丢弃冷数据(长期未访问的页),从而降低缺页异常(Page Fault)和磁盘I/O。
LRU 的优化目标
最大化热数据的驻留:频繁访问的页应尽量保留在内存中。
最小化冷数据的干扰:长期未使用的页应优先被回收。
降低缺页异常(Page Fault):避免因误回收导致进程反复从磁盘加载数据。
二、Linux LRU的实现原理与底层逻辑
Linux未使用传统单向LRU链表,而是采用改进的"近似LRU"(Two-List Strategy) 以提高效率并减少锁争用:
1. 核心设计:双列表结构
所有内存页按访问频率分为两类:
活跃列表(Active List):存放近期被访问过的页("热数据"),不易被回收。
非活跃列表(Inactive List):存放近期未被访问的页("冷数据"),优先被回收回收。
inux 将内存页分为 4 类 LRU 链表,按访问频率和类型划分:
LRU 类型 存储内容 回收优先级
ACTIVE_ANON 活跃的匿名页(如进程堆、栈) 低
INACTIVE_ANON 非活跃的匿名页 中
ACTIVE_FILE 活跃的文件页(如缓存的文件数据) 低
INACTIVE_FILE 非活跃的文件页 高
每个列表进一步划分为:
ACTIVE_ANON / INACTIVE_ANON → 匿名页(如进程堆、栈数据)
ACTIVE_FILE / INACTIVE_FILE → 文件页(如缓存的文件内容)
2. 页的访问追踪:PG_referenced标志
每次访问页时,硬件触发缺页中断,内核设置该页的PG_referenced标志位(表示最近被访问过)。
扫描逻辑:内核线程(如kswapd)定期检查此标志:
若为1,说明最近被访问过 → 将页移到活跃列表尾部(成为新热点数据)。
若为0,说明未访问 → 保持原位,留待后续回收。
3. 页的状态迁移流程
新分配页-->加入Inactive List尾部-->被访问-->标记PG_referenced=1-->周期性扫描时检测到被访问-->迁移到Active List尾部-->周期性扫描未被访问-->迁移回Inactive List头部-->再次未被访问-->被回收或Swap
4. 内存回收机制(核心逻辑)
当内存不足时(如触发low_wmark水位线):
1.优先扫描非活跃列表:
回收列表头部页(最冷数据)。
若页是脏页(已修改),需先写回磁盘。
2.若回收不足,再扫描活跃列表:
将多次扫描未被访问的活跃页降级到非活跃列表(refault distance机制)。
3.平衡逻辑:kswapd会动态调整扫描比例(如"swappiness"参数),控制回收匿名页与文件页的权重。
三、实际案例解析:MySQL数据库与文件缓存
场景:
假设系统运行MySQL服务,内存中缓存了数据库索引(文件页)。
同时有多个用户进程通过堆栈处理数据(匿名页)。
LRU处理过程:
1.初始状态:
索引文件被访问 → 缓存在ACTIVE_FILE列表。
用户进程数据 → 位于ACTIVE_ANON列表。
2.内存不足发生时:
kswapd优先扫描INACTIVE_FILE列表(如缓存的日志文件)。
若日志长期未读(PG_referenced=0),其页被回收释放内存。
3.避免误回收:
若MySQL索引被频繁访问(PG_referenced持续置1),会被保留在ACTIVE_FILE中。
某个后台进程的长期未访问数据会从ACTIVE_ANON降级到INACTIVE_ANON并被回收。
结果:
MySQL热数据保留在内存 → 查询命中内存缓存,响应极快。
冷数据被高效换出 → 减少无意义的磁盘交换操作。
四、工程优化:为什么不是标准LRU?
Linux采用近似LRU是工程权衡的结果:
传统LRU Linux LRU
需严格排序每次访问,开销大 只记录最近是否被访问(≈O(1)复杂度)
全局锁竞争激烈 通过双列表减少锁粒度
对突发访问敏感(Bélády异常) 非活跃列表作为缓冲区,避免活跃数据误回收
无法区分文件/匿名页 按类型分层管理,提升回收精度
五、查看LRU行为的工具
# 查看各LRU列表大小
cat /proc/meminfo | grep -i active
> Active(anon): 523776 kB
> Inactive(anon): 876544 kB
> Active(file): 1843200 kB
> Inactive(file): 3450880 kB
# 统计页面扫描情况(高数字表示频繁回收)
grep pgscan /proc/vmstat
> pgscan_kswapd 19230
> pgscan_direct 450
总结:LRU在Linux中的价值
核心目标: 在高负载下维持内存分配效率,避免系统"卡死"。
工程智慧: 用简化的标志位和双列表,达成接近理想LRU的效果,同时控制计算开销。
持续演进: 新机制如Workingset Refault进一步优化冷热页判断,在超大规模内存场景(如云服务器)中尤为重要。
通过这种设计,Linux内核在不引入显著开销的情况下,有效地将频繁使用的数据保留在内存中,最大化硬件资源的利用率。