背景
kmemleak 周期性地扫描内存,找出没有任何指针指向的疑似泄漏对象。扫描根除了 data/bss 段、per-cpu 区域和 task stack,还包括 struct page 数组:每个 online 且正在使用的页,它的 struct page 都可能保存着指向其他对象的指针,因此都要扫一遍。
扫法是逐页进行:扫描器遍历 zone 里的每个 PFN,跳过空洞、不属于本 zone 和空闲的页,把合格的页逐个交给 scan_block()。scan_block() 全程持有 kmemleak_lock 并关中断,于是每扫一个 64 字节的 struct page,就要完整地走一次取锁与放锁。
逐页扫描的取锁开销
┌──────────────────────────────────────┐
│ kmemleak 扫描根包含 struct page 数组 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 逐页调用 scan_block,每页仅 64 字节 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ scan_block 全程持 kmemleak_lock 且关 │
│ 中断 │ 每个 online PFN 都要抢一次锁
└──────────────────────────────────────┘
问题
- struct page 逐页交给 scan_block(),每页都要抢一次 kmemleak_lock。
- 每次取锁只扫 64 字节,取锁次数等于 online PFN 的数量。
- 取锁的固定开销在 debug 内核上被放大,整轮扫描明显拖慢。
方案
把相邻的合格页攒成一段,整段交给 scan_block() 一次扫完,而不是每页一次。
跳过条件维持不变:PFN 空洞、不属于本 zone、不在使用的页依然不扫。改动在于,相邻的合格页先攒成一段,遇到不合格页、地址不再相邻或段长满 MAX_SCAN_SIZE 时,把攒好的段一次扫掉,再从当前页起新的一段。MAX_SCAN_SIZE 为 4 KiB,一批最多 64 个 struct page,取锁次数从页数降为段数。
相邻合格页攒段批量扫描
┌────────────────────────────────────────┐
│ 遍历 zone 的 PFN,跳过空洞与不合格页 │
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ 相邻合格页攒成一段,最多 MAX_SCAN_SIZE │ 4 KiB,即 64 个 struct page
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ 段满或断开时,一次 scan_block 扫整段 │ 取锁次数从页数降为段数
└────────────────────────────────────────┘
单次持锁时间的上界并没有因此变大。scan_large_block() 扫 data 段与 per-cpu 区域时,本来就按 MAX_SCAN_SIZE 分段持锁,这里只是把 struct page 扫描对齐到同一粒度;task stack 扫描更是一次交出整个 THREAD_SIZE,arm64 与 x86_64 上不低于 16 KiB,是 MAX_SCAN_SIZE 的四倍。scan_block() 逐指针检查 scan_should_stop(),kmemleak 被禁用、扫描线程被要求停止或进程有挂起信号时都会中途退出,扫长段同样可被打断。
收益
作者在 arm64 VM(24 GiB 内存、约 17 GiB 在用)上,以 Meta 的 debug 内核配置测得:
| 指标 | Before | After | 改善 |
|---|---|---|---|
| struct page 阶段 scan_block() 调用次数 | 4390k 次 | 69k 次 | -98.4% |
| 整轮扫描耗时 | 未给出绝对值 | 未给出绝对值 | 约 -20% |
收益来自每次取锁的固定开销,未启用 lock debug 选项的内核上扫描时间基本不变。扫描覆盖范围不变:作者用临时断言核对过,每个 zone 每轮扫描实际交给 scan_block() 的页数与合格页数完全一致,包括扫描期间恰好有内存被释放的情形。