《深入理解Linux内核》第十七章:深入理解 Linux 页框回收机制
关键词:页框、LRU、kswapd、direct reclaim、Zoned Page Frame Allocator、inactive list、active list、swap、内存压力、回收策略
一、概述:为什么需要页框回收
1.1 页框的概念
Linux 将物理内存划分为固定大小的页(通常为4KB),称为页框(page frame)。页框用于存储用户进程数据、页缓存、内核数据结构等。
1.2 内存不足的问题
当系统分配内存时(如 fork、mmap、alloc_pages 等),如果可用页框不足,内核必须释放一部分页框以腾出空间 —— 这就是页框回收机制的由来。
二、回收机制总体结构
Linux 回收内存页的主干机制有两个:
2.1 kswapd 线程(后台回收)
- 每个内存节点(NUMA)启动一个
kswapd
; - 周期性检查内存水位;
- 回收页框释放到伙伴系统;
- 回收过程是异步的,不影响前台进程。
2.2 Direct Reclaim(直接回收)
- 当进程申请内存而失败时触发;
- 当前进程负责执行回收;
- 回收失败可能触发 OOM killer。
三、内存区域与水位管理
3.1 区域划分(Zones)
Linux 将物理内存划分为多个 zone:
ZONE_DMA
:可供 DMA 使用(<16MB);ZONE_NORMAL
:标准物理内存(一般用于用户空间);ZONE_HIGHMEM
:仅用于32位系统的高地址内存;ZONE_MOVABLE
:用于大块页迁移。
每个 zone 都有自己独立的页框管理与回收水位:
struct zone {
unsigned long pages_min;
unsigned long pages_low;
unsigned long pages_high;
...
};
3.2 水位定义
high
:达到此值,系统认为内存充足;low
:低于此值,触发 kswapd;min
:极限警戒线,触发 direct reclaim。
四、页回收的数据结构:LRU 链表
4.1 LRU(Least Recently Used)
Linux 使用两级 LRU 实现回收策略:
active list
:活跃页面;inactive list
:不活跃页面。
每类页面又分为:
anon
:匿名页(如堆栈、brk);file
:文件页(页缓存);
组合构成 4 条 LRU 链:
LRU_ANON_ACTIVE
LRU_ANON_INACTIVE
LRU_FILE_ACTIVE
LRU_FILE_INACTIVE
4.2 页状态转换规则
- 新页加入 inactive;
- 被频繁访问的页提升到 active;
- 长期未访问的 active 页可能被降级。
五、回收策略:选择哪些页回收?
回收目标页通常具备以下特征:
- 长时间未被访问(通过
referenced
位判断); - 非锁页(PG_locked);
- 非脏页或可立刻写回;
- 不在内核关键路径使用。
5.1 匿名页回收
- 匿名页通常为进程堆、栈;
- 必须先交换到 swap 设备;
- 交换后 page 被回收。
5.2 文件页回收
- 通常为页缓存;
- 若未修改,直接释放;
- 若脏页,需先写回磁盘。
六、核心函数与回收路径
6.1 kswapd 线程执行路径
kswapd()
└── balance_pgdat()
└── shrink_node()
└── shrink_lruvec()
└── shrink_page_list()
6.2 direct reclaim 执行路径
__alloc_pages_slowpath()
└── try_to_free_pages()
└── shrink_node()
6.3 shrink_page_list 工作流程
- 遍历 LRU 页面;
- 调用
try_to_unmap()
尝试解除映射; - 若匿名页,swap_out;
- 若文件页且脏,writepage;
- 若 clean,直接释放。
七、页状态标志与控制逻辑
7.1 struct page 中的标志位
PageLocked() // 页面被锁定
PageDirty() // 页面已被修改
PageReferenced() // 页面被访问过
PageActive() // 在 active list 上
PageSwapBacked() // 来自匿名映射或 swap
7.2 页引用位清除
在 shrink_lruvec 中会清除 referenced 位,以便决定是否降级。
八、文件页回收的特殊机制
8.1 writeback 控制
- 脏页不可直接释放;
- 需调用
writepage()
将内容同步到磁盘; - 由 pdflush / writeback thread 负责。
8.2 延迟写回参数
/proc/sys/vm/dirty_*
系列参数;- 控制何时、多久、以何速率回写脏页;
- 避免大量 I/O 峰值。
九、内核 API 接口与调试手段
9.1 手动触发回收
void wakeup_kswapd(pg_data_t *pgdat);
int shrink_all_memory();
9.2 用户态手段
echo 1 > /proc/sys/vm/drop_caches
:回收页缓存;vmstat
:监视页活动与回收;top
/free
:内存实时查看;perf record -e kswapd
:分析内存压力。
十、与 OOM Killer 的关系
- 如果页回收仍无法满足内存请求;
- 系统会触发
out_of_memory()
; - 调用
oom_kill_process()
选择并终结进程; - 参考
/proc/sys/vm/oom_kill_allocating_task
控制策略。
十一、源码路径参考
路径 | 作用描述 |
---|---|
mm/vmscan.c |
kswapd / shrinker 回收主逻辑 |
mm/page_alloc.c |
页框分配与伙伴系统实现 |
mm/swap.c |
匿名页交换到 swap 的处理逻辑 |
mm/swap_state.c |
swap entry 管理 |
include/linux/mm.h |
页结构体与回收相关定义 |
include/linux/vmstat.h |
内存统计数据 |
十二、小结
- 页框回收是 Linux 保持内存可用性的关键机制;
- 通过 LRU、状态位、匿名与文件页分类实现高效控制;
kswapd
处理轻度压力,direct reclaim
负责紧急处理;- 配合 swap、writeback 可实现完整页生命周期管理;
- 参数可调,调试工具丰富,是系统稳定性的核心一环。