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. 触发机制与步骤

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

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

  • x86/x86_64:#PF异常(IDT 14),入口page_fault,C实现do_page_fault,CR2寄存器存异常地址。
  • ARM/ARM64:Data Abort/Translation Fault,入口do_mem_abortdo_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、非法访问等所有内存异常。

六、动手实验最佳实践

  1. 简单测试建议在代码结尾加sleep,方便查PID和追踪:
sleep(10);
  1. 配合set_ftrace_pid、bpftrace进程名过滤,只抓自己进程的Page Fault。
  2. 配合function_graph tracer、tracepoint、perf,多角度观察Page Fault的产生、调用链和统计。
  3. 增大trace buffer避免短进程事件丢失。
  4. 建议将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,不只是“异常”或“出错”,更是现代操作系统内存高效管理和安全防护的核心机制。你,现在真的理解了吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值