监控工具常读的接口
监控工具
周期性读取
/proc/pid/smaps_rollup
单文件汇总
进程内存统计
一次读到全部
读取过程中,VMA 与页表遍历都在内核里完成
旧实现:进门先拿 mmap_lock 读锁
mmap_lock
读锁
逐 VMA 走页表
VMA 与页表
聚合数据
全程不放锁
VMA 多、页表大的进程,一次读取占锁很久
写者被堵在门外
mmap
建映射
munmap
拆映射
mprotect
改权限
smaps_rollup 成了 mmap_lock 长持有的典型来源
为什么占锁久
VMA 多
地址空间碎片化
页表大
逐页聚合
读锁全程不放
两者相乘,读锁持有时间被拉长
兄弟接口早已改用同一套组合
maps
RCU + per-VMA lock
smaps
RCU + per-VMA lock
numa_maps
RCU + per-VMA lock
smaps_rollup
仍全程持 mmap_lock
RCU 保护 VMA 树,per-VMA lock 稳住当前 VMA 与页表
show_smaps_rollup() 的新流程
RCU read lock
保护 VMA 树
proc_get_vma()
逐个取 VMA
per-VMA read lock
锁住当前 VMA
拿不到锁,才回退 mmap_lock
锁住之后
walk_page_range_vma()
per-VMA lock 下可调用
逐页聚合
当前 VMA 的页表
页表遍历换了接口,不再要求 mmap_lock
不再进门就锁 mmap_lock
proc_get_vma() 原本只为 maps 服务
proc_get_vma()
maps 序列接口的 helper
额外返回 gate VMA
供 maps 报告
maps 专用行为
别的接口用不了
把专用行为挪回调用方
gate VMA 逻辑
回到 maps 自己的调用方
干净的取 VMA 接口
smaps_rollup 直接复用
同一条 RCU 加 per-VMA lock 路径,两个接口共用
per-VMA 锁拿不到时
回退 mmap_lock
拿不到 per-VMA 读锁
mmap_lock 也有写者
竞争仍在
回退不是终点,锁一样可能抢不到
竞争时的减压动作
临时放锁
让 writer 先走
下一个 VMA
再试 per-VMA lock
最坏再回退一次
不会更糟
放锁窗口只延续到处理下一个 VMA
放锁窗口里,VMA 树可能已变
已报 VMA 增长或合并
从 last_vma_end 处续采
被释放 / 没有后继
自然收场
旧实现为四种情形准备长注释,如今只剩合并续扫要特判
竞争基准 · busyduration 2 秒
20 次采样
每次读取 smaps_rollup
单位:秒
Before / After 同口径
竞争场景下测读取延迟
指标
Before
After
改善
中位延迟
0.174
→
0.007
降 96.0%
最差延迟
5.220
→
3.225
降 38.2%
最好延迟
0.161
→
0.007
降 95.7%
读锁范围从全程收窄到一个 VMA
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x