硬页错误数需通过差分/proc/self/stat中第12字段maj_flt获得:该字段为累计值,须两次采样求差并除以时间间隔;推荐1秒固定间隔采样,避免过高或过低频率。

硬页错误(major page fault)在 Linux 上的来源
硬页错误发生在进程访问的页面不在物理内存中,且需要从磁盘(如 swap 或可执行文件)加载时。Linux 内核通过 /proc/[pid]/stat 和 /proc/[pid]/status 暴露统计信息,但注意:min_flt 和 maj_flt 字段记录的是**自进程启动以来的累计次数**,不是实时速率。
所以“每秒次数”必须靠两次采样做差分计算,不能直接读取一个瞬时值。
如何从 /proc/self/stat 提取累计硬页错误数
/proc/self/stat 是当前进程自身的状态快照,第 12 个字段(从 1 开始计数)是 maj_flt,即累计硬页错误数。
实操要点:
-
maj_flt是无符号 long 类型,需用%lu或std::stoul解析(C++11+) - 字段之间以空格分隔,但命令行参数(第 2 个字段)可能含空格,因此不能简单按空格 split;推荐用
std::istringstream配合跳过前 11 个字段 - 读取失败时(如文件被删、权限不足),应返回 -1 或抛异常,避免静默错误
示例片段(C++17):
std::ifstream stat("/proc/self/stat");
if (!stat.is_open()) return -1;
std::string dummy;
for (int i = 0; i < 11; ++i) stat >> dummy; // 跳过前 11 个字段
unsigned long maj_flt;
stat >> maj_flt;
每秒硬页错误率的可靠采样逻辑
差分频率太低(如间隔 5 秒)会导致分辨率不足;太高(如 10ms)则系统开销大,且 maj_flt 变化本身不频繁,高频采样意义不大。
建议做法:
立即学习“C++免费学习笔记(深入)”;
- 固定间隔 1 秒采样一次,用
std::this_thread::sleep_for(1s)控制节奏 - 至少连续采样 2 次,才能算出首秒增量;更稳的做法是维持滑动窗口(如最近 3 秒的 delta 平均)
- 注意:若进程刚启动,
maj_flt可能为 0,首次差值为 0 不代表没发生,只是尚未触发 - 硬页错误通常突发性强(如 mmap 大文件后首次访问),单秒值可能为 0 或突然跳变几十上百,不宜假设平稳增长
为什么不用 getrusage(RUSAGE_SELF, &ru)?
getrusage 的 ru_majflt 字段也提供累计硬页错误数,但它和 /proc/self/stat 的 maj_flt **本质相同**,都是内核维护的同一计数器。
区别在于:
-
getrusage是 syscall,有轻微开销;/proc/self/stat是伪文件读取,实际由内核在打开时动态生成,两者性能差异可忽略 - 某些容器环境(如 PID namespace 被隔离)下,
/proc/self/stat更可靠;而getrusage在部分 musl libc 或 seccomp 限制下可能不可用 - 没有理由优先选
getrusage—— 它不提供“每秒”能力,仍需手动差分


















