Linux下读取/proc/self/stat第10字段minflt和第12字段majflt获取累计软硬缺页数,需两次采样做差并用clock_gettime(CLOCK_MONOTONIC)计时求速率,不可依赖/proc/self/status或perf stat。

Linux下读取/proc/[pid]/stat里的缺页字段最直接
软缺页(minor faults)和硬缺页(major faults)次数就藏在/proc/self/stat的第10和第12个字段里,分别是minflt和majflt。它们是自进程启动以来的累计值,不是瞬时速率——所以你要自己做差值+计时。
注意:字段索引从1开始数,且/proc/self/stat是空格分隔、但部分字段(如可执行文件名)可能含空格或括号,必须按规范解析:第2字段用括号包裹,之后才是连续数字字段。稳妥做法是用std::istringstream跳过前两个字段再读整数。
- 打开
/proc/self/stat后,用std::string逐字段提取,不要用scanf("%d")暴力读——会因第2字段干扰错位 - 第10个字段是
minflt(软缺页),第12个是majflt(硬缺页) - 两次读取间隔建议≥100ms,太短会导致浮点除零或精度失真
用clock_gettime(CLOCK_MONOTONIC, ...)比std::chrono更稳
std::chrono::steady_clock在某些glibc旧版本或容器环境下可能回退,而CLOCK_MONOTONIC由内核保证单调递增,更适合做精确时间差。C++11起可用,但需链接-lrt(不过多数现代链接器默认带)。
- 别用
time()或gettimeofday():秒级精度不够,且gettimeofday已废弃 - 两次
clock_gettime之间避免阻塞调用(如std::cin、大内存分配),否则测出来的是等待时间而非缺页频率 - 时间差单位转成秒时,用
double(ts.tv_sec) + ts.tv_nsec / 1e9,别用整数除法丢精度
每秒速率要自己算,/proc/[pid]/status不提供实时率
/proc/self/status里有voluntary_ctxt_switches这类统计,但缺页数只给累计值(MMUPageSize相关字段是配置,不是计数)。没有现成的“每秒硬缺页”接口——Linux内核不维护滑动窗口计数器,所有速率都得用户态计算。
立即学习“C++免费学习笔记(深入)”;
- 别指望
perf stat -e page-faults,minor-faults,major-faults实时嵌入程序:它开新子进程,无法与主逻辑同步采样 - 若需更高频(如10ms粒度),要考虑
minflt/majflt更新频率本身受内核调度影响,实际最小可观测间隔约10ms - 多线程程序中,这些字段是整个进程的合计值,无法区分线程来源
示例代码片段:读取+差分+输出每秒速率
#include <fstream>
#include <sstream>
#include <ctime>
#include <iostream>
<p>long read_page_faults(const char* field) {
std::ifstream f("/proc/self/stat");
std::string token;
// skip 1st (pid) and 2nd (comm, in parentheses) fields
for (int i = 0; i < 2; ++i) f >> token;
// now read up to target field (1-indexed: minflt=10th, majflt=12th)
long val = 0;
for (int i = 3; i <= 12; ++i) {
if (i == 10 && field == std::string("minflt")) {
f >> val;
break;
} else if (i == 12 && field == std::string("majflt")) {
f >> val;
break;
} else {
f >> token;
}
}
return val;
}</p><p>int main() {
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
long min1 = read_page_faults("minflt"), maj1 = read_page_faults("majflt");</p><pre class='brush:php;toolbar:false;'>// ... your workload here ...
clock_gettime(CLOCK_MONOTONIC, &end);
long min2 = read_page_faults("minflt"), maj2 = read_page_faults("majflt");
double dt = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
std::cout << "soft/sec: " << (min2 - min1) / dt << "\n";
std::cout << "hard/sec: " << (maj2 - maj1) / dt << "\n";}
真正难的不是读数,而是确保两次采样之间没被信号中断、没发生fork、没被cgroup限频——这些都会让差值失真。生产环境监控建议加if (dt < 0.05) return;兜底。


















