kmemleak 的扫描根
data/bss
per-cpu
task stack
struct page 数组
每个 online 且在用的页,struct page 里都可能藏着指针
扫 struct page 数组:逐个 PFN
遍历 zone 的 PFN
按物理页号
跳过不合格页
空洞 / 非本 zone / 空闲
合格页交给 scan_block
逐页调用
每次处理多少
64 字节
一个 struct page
scan_block() 全程持锁
kmemleak_lock + 关中断
锁的粒度本来只有 64 字节
旧法:每页一次 scan_block
一个合格页
64 字节
scan_block()
取锁 + 关中断
放锁
再处理下一页
相邻的页也各扫各的
每次取锁只干这么点活
kmemleak_lock
全程持有,且关中断
一次只扫 64 字节
取锁固定开销被放大
debug 内核上,这个固定开销尤其明显
取锁次数等于什么
online PFN 数
每个 PFN 抢一次锁
debug 内核整轮扫描
被固定开销拖慢
页越多,白花的取锁时间越多
相邻的合格页攒成一段
合格页
地址相邻
攒成一段
连续多页
一次 scan_block
整段扫完
跳过条件不变:空洞、非本 zone、空闲页仍不扫
什么时候把段交出去
遇到不合格页
跳过该页,先结清已攒的段
地址不再相邻
断点处切分,起新段
段长满 MAX_SCAN_SIZE
4 KiB 封顶,起新段
三种情况都立即 flush,再从当前页起新的一段
一段最多多少
MAX_SCAN_SIZE = 4 KiB
一段上限
64 个 struct page
64 × 64 字节
取锁次数从页数降为段数
MAX_SCAN_SIZE 不是新粒度
scan_large_block()
data 段 / per-cpu 已按它分段
task stack
一次交出整个 THREAD_SIZE
struct page 扫描只是对齐到同一粒度
持锁上界
THREAD_SIZE ≥ 16 KiB
arm64 / x86_64,是 4 倍 MAX_SCAN_SIZE
scan_should_stop() 逐指针检查
禁用 / 停止 / 有挂起信号都可中断
扫长段同样可被打断,不会拖住系统
单次持锁时间没有变大
arm64 VM · 24 GiB 内存
约 17 GiB 在用
Meta debug 内核配置
struct page 阶段
只数 scan_block() 调用
整轮扫描耗时另算
scan_block() 调用次数
Before
After
改善
4390k 次
→
69k 次
−98.4%
扫描覆盖范围不变
每 zone 每轮
交给 scan_block 的页数 = 合格页数
含扫描期间
恰好被释放的页
作者用临时断言逐 zone 核对过
整轮扫描耗时 约 −20%
非 lock debug 内核基本不变
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x