Page Fault,你真的理解了吗?

Page Fault,你真的理解了吗?

📖 🎥 B 站博文精讲视频:点击链接,配合视频深度学习

Page Fault,你真的理解了吗?

一、开篇思考

程序崩溃提示“Segmentation fault”时,背后的真实原因是什么?为什么新分配的内存初次访问时系统会“停一下”?Page Fault 与内存管理、按需分配、进程保护的真实联系是什么?发生缺页异常时,硬件与内核各自完成了什么工作?不同CPU架构下Page Fault有无本质区别?如何用bpftrace/trace/ftrace等工具精准追溯Page Fault的发生和处理?怎样用代码和工具验证和实验Page Fault原理?

二、Page Fault 的本质与意义

1. 概念

Page Fault(缺页异常/缺页中断),指进程访问虚拟地址时,该地址未被映射到物理内存(或权限不符),由CPU检测、自动进入内核异常处理的机制。它是虚拟内存、进程隔离、内存高效利用的基础。

2. 设计目的

支持“按需分配”,降低物理内存压力。支持Swap(交换分区/文件),实现超量内存。支持COW(写时复制)、mmap延迟映射等。强化进程隔离和内存保护,防止非法访问。

3. 分类

Minor Page Fault:页已分配,只需建立页表,无磁盘IO。Major Page Fault:需从磁盘(如Swap、文件)读入。Invalid Page Fault:非法访问,通常SIGSEGV终止进程。

三、Page Fault 的发生流程与架构实现

1. 触发机制与步骤

用户态程序访问某虚拟地址。MMU查页表,无有效映射,CPU立即触发Page Fault(同步异常)。CPU硬件自动切换到内核态,跳入异常处理入口(如x86的IDT 14号,ARM的Data Abort向量)。汇编入口保存现场,跳转到C实现(如do_page_fault)。内核判断异常原因(按需分配、COW、swap、权限、非法等)。合法则分配/调页/更新页表,非法则发信号终止进程。正常时返回用户态继续执行。

2. 各主流架构下的实现差异

x86/x86_64:#PF异常(IDT 14),入口page_fault,C实现do_page_fault,CR2寄存器存异常地址。ARM/ARM64:Data Abort/Translation Fault,入口do_mem_abort→do_page_fault,FAR/ESR等寄存器存信息。RISC-V:Page Fault异常,机制相同,入口与寄存器命名不同。不同架构保存现场、异常号、寄存器有差异,但本质机制一致。

四、代码与实验示例

1. 非法访问实验

int *ptr = (int*)0x123456; // 非法地址

*ptr = 42; // 必然触发Page Fault,最终SIGSEGV

运行strace ./no_correct看到最后SIGSEGV信号,但不直接显示Page Fault细节。用bpftrace追踪异常地址:

sudo bpftrace -e 'tracepoint:exceptions:page_fault_user { printf("Page fault from PID %d at address 0x%lx\n", pid, args->address); }' | grep 0x123456

可以直接抓到异常访问的Page Fault事件。

2. 按需分配实验

char *buf = malloc(4096*10); // 只分配虚拟空间

buf[0] = 'A'; // 第一次写入,Page Fault分配物理页

buf[4096] = 'B'; // 跨页访问,再次Page Fault

用perf stat -e page-faults ./page_fault,可见多次Page Fault(每新页一次)。用bpftrace可抓到每次Page Fault的地址。

3. 配合 ftrace/function_graph 追溯内核调用链

# 设置只跟踪do_page_fault及其调用链

# 可用set_ftrace_pid只看你自己的进程

echo 0 > /sys/kernel/debug/tracing/tracing_on

echo nop > /sys/kernel/debug/tracing/current_tracer

echo do_page_fault > /sys/kernel/debug/tracing/set_graph_function

echo function_graph > /sys/kernel/debug/tracing/current_tracer

echo 12345 > /sys/kernel/debug/tracing/set_ftrace_pid # 替换为你的PID

echo 1 > /sys/kernel/debug/tracing/tracing_on

./page_fault

cat /sys/kernel/debug/tracing/trace > trace.log

trace.log 展示 do_page_fault → handle_mm_fault 等详细调用层级,结合源码可分析真实分配、异常、回页等分支。

五、Page Fault 相关机制与常见误区

1. Page Fault 并不等价于“出错”

绝大多数Page Fault是正常的:进程首次访问内存、按需分配、fork写时复制、mmap延迟加载。只有非法越界、权限错误才会变成“错误”。

2. 内存分配=虚拟地址分配+首次访问才Page Fault

malloc/new 只分虚拟空间,不会立即分物理内存。首次写入才会由Page Fault自动分配物理页。

3. do_page_fault是“内核虚拟内存统一入口”,不只分配

处理按需分配、COW、swap、非法访问等所有内存异常。

六、动手实验最佳实践

简单测试建议在代码结尾加sleep,方便查PID和追踪:

sleep(10);

配合set_ftrace_pid、bpftrace进程名过滤,只抓自己进程的Page Fault。配合function_graph tracer、tracepoint、perf,多角度观察Page Fault的产生、调用链和统计。增大trace buffer避免短进程事件丢失。建议将trace结果输出到文件,配合grep、less分析。

七、典型核心知识串讲

Page Fault为同步异常,和CPU执行指令同步发生。内核分minor/major/invalid,后台swap和COW都靠它实现。不同架构流程差异仅在异常入口和寄存器保存,主流程一致。strace只能看到信号,ftrace/perf/bpftrace等工具才能深入分析调用链和事件本身。do_page_fault、handle_mm_fault、do_anonymous_page等才是实际分配、回页、异常分支的关键。

八、结语

Page Fault是现代虚拟内存和内存保护的基石。掌握Page Fault原理和追踪手段,不仅有助于性能优化、定位疑难问题,也是内核开发与系统调优的必备能力。

Page Fault,不只是“异常”或“出错”,更是现代操作系统内存高效管理和安全防护的核心机制。你,现在真的理解了吗?

相关推荐

GREE(格力)净水器报价
365bet线

GREE(格力)净水器报价

📅 10-07 👁️ 723
如何正确设置无线路由器服务名?
华为怎么进BT365

如何正确设置无线路由器服务名?

📅 07-04 👁️ 6007
哪些手机自带位置穿越 手机自带位置穿越功能的好处