Linux下获取硬页错误需读取/proc/pid/stat的第12个字段majflt,而非statm或status;须定时采样求差再除以时间间隔,注意解析时跳过含空格的命令名字段。

Linux下用/proc/pid/statm和/proc/pid/status无法获取硬页错误
硬页错误(major page fault)不是靠读取 /proc/pid/statm 或 /proc/pid/status 能拿到的——这两个文件只提供内存占用、RSS、共享页等静态快照,不包含页错误计数。真正记录硬页错误的是 /proc/pid/stat 的第12个字段(majflt),它表示**进程自启动以来累计发生的硬页错误总数**。
从/proc/pid/stat提取majflt并计算每秒增量
要得到“每秒硬页错误次数”,必须做两件事:定时读取 stat 文件、两次采样求差、再除以时间间隔。注意以下关键点:
-
/proc/pid/stat是空格分隔的纯文本,字段数固定但字段长度可变(如命令名含空格时会被括号包裹),不能简单按空格切分;必须按规范解析:第12个字段(索引从1开始)是majflt,位置固定 - 推荐用
std::ifstream逐行读 +std::istringstream提取前12个字段,跳过命令名(第2字段,被括号包围)避免解析错位 - 两次采样间隔建议 ≥100ms,太短会导致增量为0(硬页错误本身不频繁,且内核只在缺页处理完成时更新计数)
示例片段(仅核心逻辑):
long long get_majflt(pid_t pid) {
std::ifstream f("/proc/" + std::to_string(pid) + "/stat");
std::string line;
if (!std::getline(f, line)) return -1;
std::istringstream iss(line);
std::string dummy;
// 跳过第1字段(pid)、第2字段(comm,含括号)、第3字段(state)
iss >> dummy >> dummy >> dummy;
for (int i = 4; i <= 11; ++i) iss >> dummy; // 跳过第4~11字段
long long majflt;
iss >> majflt;
return majflt;
}
硬页错误统计受内核版本和权限影响
不是所有进程都能被任意用户读取其 /proc/pid/stat ——尤其当目标进程属于其他用户时,/proc/pid/ 目录默认权限为 dr-xr-xr-x,但 stat 文件本身权限是 -r--r--r--,只要目录可进入就能读。不过从 Linux 4.1 开始,若启用了 hidepid=2(常见于容器或加固系统),非同组/非特权用户将无法访问其他用户的 /proc/pid/ 目录,此时会直接返回 ENOENT 或 EACCES。
立即学习“C++免费学习笔记(深入)”;
- 普通用户只能监控自己启动的进程;监控系统级进程(如
pid=1)需 root 权限 - 硬页错误计数由内核在每次 major fault 处理完成后原子递增,无竞态问题,但采样时刻的值是“截至该时刻的累计值”,不是瞬时速率
- 不要混淆
majflt(硬页错误)和minflt(软页错误,第11字段)——后者不触发磁盘IO,通常无需关注
替代方案:用perf_event_open直接抓取PERF_COUNT_SW_PAGE_FAULTS_MAJ
如果需要更高精度或跨进程聚合(比如整个cgroup的硬页错误),可以用 perf_event_open 系统调用监听 PERF_COUNT_SW_PAGE_FAULTS_MAJ 事件。但这要求:
- 内核开启
CONFIG_PERF_EVENTS=y(绝大多数发行版默认开启) - 调用进程有
CAP_SYS_ADMIN或perf_event_paranoid ≤ 2(可通过sysctl kernel.perf_event_paranoid查看) - 需手动管理 perf event fd、mmap ring buffer、样本解析——比读
/proc/pid/stat复杂一个数量级,且对单进程监控属于过度设计
除非你在写性能分析工具或需要纳秒级时间戳关联,否则老老实实读 majflt 就够了。
硬页错误本身是低频事件,采样窗口拉太短没意义;而 majflt 字段不会溢出(long long 类型),但要注意两次采样间进程可能已退出——读 /proc/pid/stat 失败时得跳过本次计算,别拿旧值硬减。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/shoujipingce/124080.html