缺页异常(page fault)不等于程序出错。MMU 无法按当前页表完成访问时,CPU 把控制权交给内核;内核可能分配匿名页、从文件页缓存建立映射、执行 COW,也可能判定地址或权限非法并发送信号。按需分页正是利用可修复的 fault,避免程序启动时为尚未访问的区域立即准备全部物理页。
先区分 TLB miss
TLB miss 通常由硬件页表遍历解决,不一定进入内核;page fault 则表示遍历结果无法满足这次访问,例如页表项尚未建立、页面不在内存、写入只读 COW 映射或权限检查失败。把 perf 中的 TLB miss 数直接当成缺页次数,会混淆两个不同层次。
minor 与 major 的含义
minor fault 不需要为页面发起存储 I/O,例如匿名页首次分配、已有 page cache 的文件页建立 PTE、或某些 COW 情况。major fault 通常意味着需要等待后备存储读入页面。二者都是进程记账分类,不直接等于“快”和“慢”的固定时间;NUMA、内存回收、锁竞争和存储设备都会改变代价。
用同一工作负载观察
1 | /usr/bin/time -v ./your_app 2>&1 | grep -E 'Major|Minor' |
/proc/<pid>/stat 的字段位置固定但命令名可能包含空格和括号,生产脚本不宜用上面这种简单 awk 解析;更可靠的采集程序应按 proc 文档处理完整格式。对延迟敏感任务,还应记录 fault 发生的阶段和调用栈,而不只看进程退出后的累计次数。
从 fault 类型回到根因
- 启动阶段 minor fault 很多,可能只是按需建立匿名页或装入共享库,不必立即优化。
- 稳态控制循环仍发生 fault,需要检查是否预触页、是否动态扩展堆栈、映射是否被回收,以及是否错误依赖按需分配。
- major fault 增长要检查文件访问、内存压力、page cache 与 swap,而不是只调高线程优先级。
SIGSEGV可能来自地址不存在或权限不符;SIGBUS还可能与截断后的文件映射等对象错误相关。
证据边界
累计 fault 计数不能定位具体源代码,也不能单独证明内存不足。严谨分析应保存内核版本、映射、内存压力、page cache/swap 状态,并使用 perf/ftrace/eBPF 等工具关联 fault 与调用路径。
参考:Page tables · proc_pid_stat(5) · getrusage(2) · 不懂缺页异常与页表遍历,别再说你懂 Linux 内核了