linux下通过/proc/pid/stat第10、12字段读取软硬缺页次数,需跳过comm括号字段,用steady_clock定时采样计算速率,输出csv供gnuplot绘图,并检查pid存在性与权限。

Linux下用/proc/pid/stat读取缺页中断计数
Linux内核把每个进程的软缺页(minor faults)和硬缺页(major faults)次数,实时记录在 /proc/<code>pid/stat 的第10和第12个字段(从1开始计数)。注意:不是第9和第11,因为第1个是pid、第2个是comm(括号包围的进程名),导致字段偏移容易错。
- 字段10(
min_flt):累计软缺页次数(页已在内存中,仅需映射) - 字段12(
maj_flt):累计硬缺页次数(需从磁盘加载页) - 必须按空格分割后跳过括号字段——
comm字段形如(bash),会吞掉一个空格,实际要用正则或逐字段解析,不能简单split(' ') - 读取频率建议 ≥100ms 间隔,太密会干扰统计本身;1s采样足够生成每秒频率
用C++定时采样并计算每秒增量
直接调用 std::this_thread::sleep_for 控制采样周期,两次读取差值除以时间间隔即得瞬时速率。关键点是避免整数溢出(计数是 unsigned long)和采样抖动。
- 用
std::ifstream读/proc/<code>pid/stat,每次打开-关闭,不缓存文件句柄(/proc是虚拟文件系统,无开销) - 保存上一次的
min_flt和maj_flt值,与当前值做无符号减法(确保不会因回绕误判负增长) - 时间间隔用
std::chrono::steady_clock,别用system_clock(可能被NTP校正干扰) - 示例片段:
unsigned long prev_min = 0, prev_maj = 0;<br>auto prev_time = steady_clock::now();<br>// ... 在循环中<br>auto curr_time = steady_clock::now();<br>double dt_sec = duration_cast<duration>>(curr_time - prev_time).count();<br>double min_rate = (min_flt - prev_min) / dt_sec;<br>double maj_rate = (maj_flt - prev_maj) / dt_sec;</duration>
生成图表需导出数据而非内嵌绘图
C++标准库没有图形能力,强行链接OpenGL或Qt绘图既重又偏离目标。正确做法是把每秒速率输出为CSV或TSV,交给外部工具可视化。
- 每行输出格式建议:
timestamp,minor_faults_per_sec,major_faults_per_sec - 用
std::cout或写入文件,加std::flush避免缓冲延迟(尤其重定向到管道时) - 配合
gnuplot实时绘图:启动命令如./pagerate | gnuplot -e "set terminal wxt; plot '-' using 1:2 with lines title 'minor', '' using 1:3 with lines title 'major'" - 若需保存图表,改用
set terminal png并重定向输出,但注意gnuplot需提前创建输出目录
注意PID变化和权限问题
目标进程可能退出、fork或被systemd托管导致PID复用,/proc/pid 路径瞬间失效;同时非root用户无法读取其他用户的 /proc/<code>pid/stat。
- 采样前先检查
access("/proc/<code>pid/stat", R_OK) == 0,失败则退出或报错,不静默跳过 - 对自身进程可用
getpid();监控他人进程时,优先用pgrep获取PID,并监听/proc/<code>pid/status 的State:字段是否为R或S(排除Zombie) - 硬缺页突增往往伴随IO瓶颈,但单看数字无法区分是mmap文件缺页还是匿名页交换——需结合
/proc/<code>pid/maps 和iostat综合判断
真实场景里,软缺页每秒几千次很正常,硬缺页超过10次/秒就值得排查;但数值本身没意义,趋势和突变点才是关键。别忘了加信号处理(SIGINT)让程序能干净退出,否则临时文件或gnuplot子进程可能残留。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











