mincore(2):查一段地址的驻留状态
mincore(2)
查询页是否驻留
结果向量
每页一个字节
最低位置 1
页在内存中
谁在用
应用进程
判断哪些页被换出或未加载
数据库
确认缓存页还在内存
查询是只读的,不改变页的内容
内核怎么查
找到起始 VMA
按起始地址
走它的页表
逐 PTE 判断
PTE present + page cache
写结果向量
每轮迭代都锁全局
mmap_read_lock()
锁定整个地址空间
do_mincore()
查找 + 走页表
拷贝结果
然后放锁
结果缓冲区只有一页,一轮最多处理 PAGE_SIZE 个页
大片区域怎么办
每轮 PAGE_SIZE 页
临时缓冲区一页
下一轮重新加锁
再申请一次 mmap 读锁
循环到扫完
锁反复申请与释放
查询范围越大,加锁次数越多
公平读写锁的连带代价
writer 排队
mmap / munmap / mprotect
后来的读者也排队
只读查询被迫等待
mincore 只访问一个 VMA,锁范围却是整个地址空间
锁的范围远超实际需要
mmap_lock 读锁
整个地址空间的 VMA 全锁住
per-VMA 读锁
只锁目标 VMA
do_mincore() 只访问起始地址所在的那一条 VMA
mincore 天然适合 per-VMA lock
只读查询
不修改 VMA 与页表
一次一条 VMA
处理完就放锁
无需全局锁
范围正好匹配
per-VMA lock 的语义覆盖这两点
改动一:页表遍历的锁模式
PGWALK_RDLOCK
要求持 mmap 读锁
PGWALK_VMA_RDLOCK_VERIFY
要求持目标 VMA 读锁,遍历时校验
遍历框架不再依赖 mmap_lock
改动二:系统调用循环
mmap_read_lock() 包住 do_mincore()
每轮都申请全局读锁
vma_start_read_unlocked()
按地址查找并锁 VMA
处理完用 vma_end_read() 释放
查找与加锁一体完成
查找 + 加锁
不留 VMA 被并发修改的窗口
地址没有 VMA 覆盖
返回 NULL,仍报 -ENOMEM
错误语义不变;do_mincore() 改为接收已锁定的 VMA
一轮迭代:只锁一条 VMA
按地址锁 VMA
vma_start_read_unlocked()
do_mincore(vma)
页表遍历校验 VMA 已锁定
vma_end_read()
放掉这一条 VMA
结果拷贝完就进入下一轮
缓冲区仍是一页
每轮 PAGE_SIZE 页
临时缓冲区一页
下一轮重新找 VMA
per-VMA lock 只覆盖这一轮
不再碰 mmap_lock
循环多少次都一样
锁粒度跟着 VMA 走,而不是跟着整个进程走
预期收益(代码逻辑推断)
完全绕开 mmap_lock
与缺页、munmap 不再同锁竞争
不再受公平性牵连
writer 排队时不必跟着等
锁定范围
从整个地址空间缩到目标 VMA
作者未提供性能数据
以上为代码逻辑推断的预期
只读查询不必再排全局锁的队
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x