Page Fault vs Segmentation Fault:真正的区别与内核处理流程


支持作者,点击京东购买《Yocto项目实战教程》


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 架构为例,当用户态程序访问虚拟地址时:

  1. CPU 检查页表,发现页未映射,触发中断 #PF(Page Fault)

  2. 进入内核:

    do_page_fault(struct pt_regs *regs, unsigned long address, unsigned int error_code);
    
  3. 内核检查该地址是否属于合法区间(如 VMA)

  4. 如果合法:

    • 调用 handle_mm_fault()alloc_pages() → 建立映射 → 返回用户态
  5. 如果非法:

    • 调用 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 FaultSegmentation Fault
是否是错误❌ 不一定✅ 是非法访问
由谁处理内核页异常处理函数内核产生 SIGSEGV 信号
是否可恢复✅ 可以(合法地址)❌ 不可恢复(直接终止进程)
是否导致崩溃❌ 不一定✅ 通常导致程序崩溃

九、一句话总结

Page Fault 是虚拟地址管理的调度机制,而 Segmentation Fault 是非法内存访问的结果。在 Linux 内核中,二者一脉相承,却职责不同,掌握它们的区别是理解 Linux 内存系统和调试问题的核心基础。


支持作者,点击京东购买《Yocto项目实战教程》


### **Page Fault(缺页中断)详解** **Page Fault(缺页中断)** 是操作系统内存管理中的一种机制,当 CPU 访问的虚拟内存页(Page)不在物理内存(RAM)时,由 **MMU(内存管理单元)** 触发的一种异常(Exception)。操作系统会捕获该异常,并尝试从磁盘(如 SSD/HDD)加载缺失的页到内存,然后重新执行引发异常的指令。 --- ## **1. Page Fault 的触发场景** Page Fault 通常发生在以下情况: | **类型** | **原因** | **处理方式** | |------------------------|-------------------------------------------------------------------------|----------------------------------| | **Minor Page Fault** | 页在内存中(如 Page Cache),但未映射到进程的页表(如共享库加载) | 快速修复(无需磁盘 I/O) | | **Major Page Fault** | 页不在内存中(如首次访问文件数据或 swap 空间) | 需从磁盘加载(高延迟) | | **Invalid Page Fault** | 访问非法地址(如空指针、越界访问) | 触发 **Segmentation Fault** | --- ## **2. Page Fault处理流程** ```mermaid sequenceDiagram CPU->>MMU: 访问虚拟地址 MMU->>页表: 查询物理页 alt 页表命中 页表-->>MMU: 返回物理地址 MMU-->>CPU: 继续执行 else 页表未命中(Page Fault) MMU->>CPU: 触发异常 CPU->>操作系统: 进入内核态 操作系统->>缺页处理程序: 调用处理逻辑 alt Minor Fault 缺页处理程序->>Page Cache: 建立映射 else Major Fault 缺页处理程序->>磁盘: 读取数据到内存 缺页处理程序->>页表: 更新映射 end 缺页处理程序-->>CPU: 恢复执行 end ``` ### **关键步骤** 1. **CPU 访问虚拟地址**,MMU 查询页表。 2. **页表未命中**(PTE 无效或不在内存),触发 Page Fault。 3. **操作系统接管**,判断缺页类型: - **Minor Fault**:页在 Page Cache,只需更新页表映射。 - **Major Fault**:需从磁盘读取数据到内存(涉及 I/O 延迟)。 4. **恢复执行**:缺页处理完成后,CPU 重新执行原指令。 --- ## **3. Page Fault 的影响** - **性能开销**: - Minor Fault:微秒级(仅更新页表)。 - Major Fault:毫秒级(磁盘 I/O,可能阻塞进程)。 - **优化手段**: - **预取(Prefetching)**:提前加载可能访问的页(如 `madvise()`)。 - **大页(Huge Pages)**:减少 TLB Miss 和缺页次数。 - **内存锁定(mlock)**:防止关键数据被换出(Swap)。 --- ## **4. 实际案例** ### **(1)程序首次访问动态库** ```c // 首次调用 printf 会触发 Minor Page Fault(共享库加载) printf("Hello, Page Fault!"); ``` - **原因**:`.so` 文件已缓存到 Page Cache,但进程页表尚未映射。 ### **(2)读取大文件** ```c FILE *fp = fopen("large_file.bin", "r"); fread(buffer, sizeof(char), 1GB, fp); // 首次读取触发 Major Fault ``` - **原因**:文件数据不在内存,需从磁盘加载。 ### **(3)非法访问** ```c int *ptr = NULL; *ptr = 42; // 触发 Invalid Page FaultSegmentation Fault) ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值