Page Fault vs Segmentation Fault:真正的区别与内核处理流程
在 Linux 系统中,很多人在程序崩溃时会看到熟悉的错误信息:
Segmentation fault (core dumped)
这似乎说明程序出现了“内存访问错误”。但背后到底发生了什么?这和操作系统中的 Page Fault(页错误) 有什么关系?
本文将从概念、触发流程、内核代码处理、异常信号以及实际案例来全面剖析 Page Fault 与 Segmentation Fault 的本质区别与联系,帮助读者理解它们在 Linux 内核中的真实工作机制。
一、概念清晰化:Page Fault 与 Segmentation Fault
✅ Page Fault 是什么?
Page Fault 是一种 正常的中断机制,指的是程序访问了一个尚未映射物理页帧的虚拟地址时,由硬件(CPU)向操作系统发出的中断。
这通常发生在:
- 第一次访问 malloc/mmap 分配的内存
- 栈自动增长
- 读取换出到磁盘的页(swap-in)
如果地址合法,操作系统会分配页帧或执行换页操作,处理完毕后程序继续执行。
❌ Segmentation Fault 是什么?
Segmentation Fault 是一种 严重的内存访问错误,指程序访问了不允许访问的虚拟地址或具有不当权限的地址,内核判定为非法后发出
SIGSEGV
信号,导致程序崩溃。
常见原因:
- 访问 NULL 指针(0x0 地址)
- 数组越界访问未映射页
- 写入只读内存区域(如
.text
段) - 释放后访问(use-after-free)
二、关系揭示:它们之间是什么关系?
✅ 一句话总结:
所有 Segmentation Fault 都是 Page Fault,但不是所有 Page Fault 都是 Segmentation Fault。
- Page Fault 是机制,属于访问虚拟页时的一次页表异常
- Segmentation Fault 是后果,是非法 Page Fault 的处理结果
三、实际流程解析:一次 Page Fault 的内核处理路径
以 x86_64 架构为例,当用户态程序访问虚拟地址时:
-
CPU 检查页表,发现页未映射,触发中断
#PF
(Page Fault) -
进入内核:
do_page_fault(struct pt_regs *regs, unsigned long address, unsigned int error_code);
-
内核检查该地址是否属于合法区间(如 VMA)
-
如果合法:
- 调用
handle_mm_fault()
→alloc_pages()
→ 建立映射 → 返回用户态
- 调用
-
如果非法:
- 调用
bad_area()
→ 向进程发送SIGSEGV
- 调用
四、代码级逻辑核心函数一览
函数名 | 作用说明 |
---|---|
do_page_fault() | Page Fault 异常入口函数 |
find_vma() | 查找虚拟地址是否落入合法区间 |
handle_mm_fault() | 执行缺页处理(如分配页、换页等) |
vmalloc_fault() | 处理 vmalloc 虚拟区间的特例 fault |
bad_area() | 地址非法或权限错误,生成 SIGSEGV 信号 |
force_sig_info() | 向当前进程发送 SIGSEGV 信号 |
五、典型案例:正常 Page Fault vs 错误 Page Fault
✅ 案例1:合法缺页
char *buf = malloc(4096);
buf[0] = 'A'; // 第一次写,触发合法 Page Fault
malloc()
仅创建虚拟地址映射;- 第一次访问时触发 Page Fault,内核分配页帧;
- 返回后程序继续运行。
❌ 案例2:非法访问
char *p = NULL;
*p = 1; // Segmentation Fault
- NULL 是无效地址(0x0)
- Page Fault 发生 → 地址不合法 → 触发
SIGSEGV
六、信号机制与用户态结果
当 Segmentation Fault 发生时,内核向进程发送:
SIGSEGV(信号编号 11)
默认行为:终止进程并生成 core dump,可通过 ulimit -c unlimited
启用。
程序崩溃提示:
Segmentation fault (core dumped)
可以用 gdb ./a.out core
调试查看崩溃位置。
七、与调试工具的联动:perf + crash + ftrace
工具 | 用途 |
---|---|
perf | 分析 page-fault 热点函数位置 |
ftrace | 跟踪 handle_mm_fault / do_page_fault 调用链 |
crash | 在内核崩溃时检查进程页表、VMA 信息 |
八、总结回顾
对比点 | Page Fault | Segmentation Fault |
---|---|---|
是否是错误 | ❌ 不一定 | ✅ 是非法访问 |
由谁处理 | 内核页异常处理函数 | 内核产生 SIGSEGV 信号 |
是否可恢复 | ✅ 可以(合法地址) | ❌ 不可恢复(直接终止进程) |
是否导致崩溃 | ❌ 不一定 | ✅ 通常导致程序崩溃 |
九、一句话总结
Page Fault 是虚拟地址管理的调度机制,而 Segmentation Fault 是非法内存访问的结果。在 Linux 内核中,二者一脉相承,却职责不同,掌握它们的区别是理解 Linux 内存系统和调试问题的核心基础。