硬页错误每秒速率需通过两次读取/proc/self/stat的majflt字段(第12列)做差分计算,配合steady_clock精确计时,推荐用sscanf解析或getrusage(RUSAGE_SELF)获取更便携准确的累计值。

硬页错误在 Linux 上实际查的是 /proc/[pid]/stat 的第 12 列(majflt)
Linux 内核并不直接暴露“每秒硬页错误数”,而是累计值。硬页错误(major fault)指需要从磁盘(如 swap 或文件 backing store)加载页面的缺页异常,对应 /proc/[pid]/stat 中的 majflt 字段(第 12 个空格分隔字段,索引从 1 开始)。它自进程启动起累计,单位是次数,**不是速率**。
要获得“每秒波动”,必须做两次采样并相减再除以时间间隔。注意:majflt 是无符号 long,溢出极罕见但理论上存在,不过实践中可忽略。
- 用
getpid()获取当前进程 PID,拼出路径如/proc/12345/stat - 读取该文件后需按空格分割,并跳过前 11 个字段(第 12 个才是
majflt);注意第 3–10 字段含括号(如(bash)),不能简单用strtok按空格切——推荐用std::istringstream配合ignore和getline跳过首字段括号,或用sscanf指定格式(见下条) -
sscanf(line.c_str(), "%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d %lu", &majflt)是最稳妥的解析方式:跳过前 11 个字段,只提取第 12 个unsigned long
用 std::chrono 控制采样间隔并计算 delta/sec
别用 sleep() + 粗略计时,精度差且无法补偿处理耗时。正确做法是记录每次读取的绝对时间点,用 std::chrono::steady_clock 计算真实经过时间。
- 第一次读取
majflt后立即记录auto t0 = steady_clock::now() - 循环中每次 sleep 前先算出下次采样应等待的时长:
auto next = t0 + seconds(1),然后this_thread::sleep_until(next) - 再次读取
majflt,用新旧值差除以真实秒数(duration_cast),得到精确的 per-second rate>(t1 - t0).count() - 若两次采样间隔远小于 1 秒(比如因调度延迟),会导致瞬时值虚高;建议最小间隔设为 0.5 秒并记录原始 delta 和 duration,不强行归一到 1 秒
权限与竞态问题:/proc/[pid]/stat 可能读失败或返回旧值
即使查自己进程,/proc/[pid]/stat 在内核中是动态生成的,读取过程可能被中断或返回部分更新数据。更严重的是:如果进程刚 fork 出子进程,父进程的 majflt 不会包含子进程的硬页错误——每个进程独立统计。
- 读
/proc/self/stat比拼接 PID 更安全(避免 PID 重用导致读错进程) - 每次读取前检查
open()或ifstream::is_open()是否成功;读取后验证是否成功提取了majflt(sscanf返回值应为 1) - 若连续多次读取
majflt不变,不一定是零错误,可能是采样太密、内存访问局部性好,或进程处于休眠状态未触发新缺页 - 不要把单次 delta/sec 当作负载指标——硬页错误往往突发(如 mmap 大文件后首次访问),需观察 5–10 秒滑动窗口均值才具参考性
替代方案:用 getrusage(RUSAGE_SELF, &ru) 更便携但字段名易混淆
getrusage 返回的 struct rusage 中,ru_majflt 字段就是硬页错误累计值,语义和 /proc/self/stat 的 majflt 完全一致,且无需解析文本、无竞态、跨 Unix 系统可用(包括 macOS)。但它不提供软页错误(ru_minflt 才是 soft fault)。
- 调用开销比读
/proc略低,因为是系统调用而非文件 I/O - 注意:Windows 不支持
getrusage,若需跨平台,Linux 下优先用此接口,Windows 下得走 ETW 或GetProcessMemoryInfo(但后者不区分 major/minor fault) - 字段名
ru_majflt容易误看成 “minor” —— 实际上ma是 major,mi才是 minor(ru_minflt)
硬页错误的“每秒波动”本质是离散累计值的差分,没有内建实时流式接口。采样频率、时间测量精度、字段解析鲁棒性,三者缺一不可;尤其当进程生命周期短于采样周期时,majflt 可能始终为 0——这不是工具不准,是现象本身不存在。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











