/proc/[pid]/stat 的第10、12字段分别记录进程累计软缺页(minflt)和硬缺页(majflt)次数,需用clock_gettime(CLOCK_MONOTONIC)精确测间隔计算频率,建议独立线程采集并用atomic同步,区分二者含义以定位性能瓶颈。

Linux 下用 /proc/[pid]/stat 读取当前进程的缺页次数
Linux 内核在 /proc/[pid]/stat 的第 10 和第 12 字段分别记录了进程的 minor page faults(软缺页)和 major page faults(硬缺页)累计次数。这两个值是自进程启动以来的单调递增计数器,不是频率,但可用来计算单位时间内的发生率。
实操建议:
- 用
getpid()获取当前pid,拼出路径如/proc/12345/stat - 逐字段解析需跳过前 9 个空格分隔字段,用
std::istringstream或strtol安全提取第 10(minflt)和第 12(majflt)个字段 - 注意:字段索引从 1 开始,且第 14 字段才是
utime,别数错;第 10 和 12 字段之间有空字段(第 11 位是cminflt,子进程软缺页),不能简单按空格切 10 次 - 示例片段:
std::ifstream f("/proc/" + std::to_string(getpid()) + "/stat");<br>std::string line;<br>if (std::getline(f, line)) {<br> std::istringstream iss(line);<br> std::string dummy;<br> for (int i = 0; i > dummy;<br> long minflt, majflt;<br> iss >> minflt >> dummy >> majflt; // 第10、11、12个字段<br>}
用 clock_gettime(CLOCK_MONOTONIC) 做稳定的时间采样间隔
缺页频率必须基于真实流逝时间计算,不能依赖 sleep() 或 usleep()——它们不保证精度,且进程可能被调度延迟,导致分母失真。
实操建议:
- 每次采样前调用
clock_gettime(CLOCK_MONOTONIC, &ts),用ts.tv_sec + ts.tv_nsec * 1e-9得到 double 秒值 - 两次采样时间差即为实际间隔 Δt,用它去除以缺页增量,得到 Hz 单位的频率(如 124.3 major faults/sec)
- 避免用
CLOCK_REALTIME:系统时间可能被 NTP 或管理员调整,造成 Δt 为负或跳变 - 最小合理采样间隔建议 ≥ 100ms;太短会导致浮点除零、统计噪声大,且
/proc/[pid]/stat更新本身有轻微延迟
区分 minor 与 major 缺页:监控目标决定是否合并
minor fault 表示页已在内存中(如刚被换入、或写时复制触发),内核只需建立页表映射;major fault 表示页不在内存,需从磁盘(swap 或文件)加载,代价高得多,通常才是性能瓶颈信号。
实操建议:
- 若监控目标是 I/O 压力或响应延迟恶化,应单独追踪
majflt,并设阈值告警(如 > 5/sec 持续 3 秒) - 若分析内存局部性或 TLB 效率,
minflt更相关,但它本身不直接反映慢速 I/O - 不要简单把两者相加:一次 major fault 可能伴随多个 minor fault(例如 mmap 大文件后首次访问多页),相加会掩盖真正昂贵的操作
-
minflt高但majflt为 0 是健康表现(说明工作集适配物理内存);反之则提示内存不足或 swap 活跃
避免轮询阻塞主线程:用独立线程 + std::atomic 同步统计值
持续读取 /proc/[pid]/stat 并计算频率不能放在主线程里做,尤其对 GUI 或实时性要求高的程序——I/O 和浮点运算虽轻,但固定间隔 sleep 会干扰主逻辑调度。
实操建议:
- 启一个分离的
std::thread,内部用while(running.load(std::memory_order_acquire))循环采样 - 用
std::atomic存储最新计算出的minflt_rate和majflt_rate,主线程只读不锁 - 避免用
std::mutex保护 float 变量——原子操作足够,且无锁更轻量;但注意std::atomic在部分旧平台可能转为 mutex 实现,可改用std::atomic存纳秒级时间戳 + 整数计数器再由主线程计算 - 线程启动后应先 sleep 一点时间(如 50ms),等主程序完成初始化后再开始首次采样,否则初值波动大
真正的难点不在读取数据,而在于理解哪些缺页是“预期之内”(比如刚启动时 mmap 的页面第一次访问)、哪些是“持续恶化”(比如每秒 major fault 稳定上升)。没有上下文的数字只是噪音;你得把频率曲线和你的分配模式、mmap 区域生命周期、以及 /proc/[pid]/status 里的 VmRSS 和 VmSwap 对齐看,才能判断是不是该优化数据结构布局或者调大 vm.swappiness。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











