Linux内核源码详解--缺页异常(Page Fault)处理的核心函数handle_pte_fault

handle_pte_fault 是 Linux 内核中处理缺页异常(Page Fault)的核心函数,负责根据页表项(PTE)的状态和访问权限,分发到不同的子处理逻辑(如匿名页映射、文件页映射、写时复制、NUMA 迁移等)。以下基于代码逻辑和搜索结果详细解析其功能、原理及处理流程。

 

一、功能概述

 

handle_pte_fault 在缺页异常处理流程中被调用(通常由 __handle_mm_fault 触发),用于处理以下场景:

 

 1.首次访问未映射的虚拟地址(PTE 为空)。

 

 2.访问已被换出(Swap Out)的页面(PTE 存在但 PRESENT 位为 0)。

 

 3.写保护触发写时复制(COW)。

 

 4.NUMA 内存页迁移优化。

 

 5.权限检查与页表状态更新。

 

二、代码逻辑分步解析

 

1. 检查 PMD 状态(大页/透明大页处理)

 

if (unlikely(pmd_none(*vmf->pmd))) {

    vmf->pte = NULL; // 延迟 PTE 分配,避免与透明大页冲突

} else if (pmd_devmap_trans_unstable(vmf->pmd)) {

    return 0; // 透明大页不稳定状态,需重试

} else {

    vmf->pte = pte_offset_map(vmf->pmd, vmf->address); // 获取 PTE

    vmf->orig_pte = *vmf->pte;

    barrier();

    if (pte_none(vmf->orig_pte)) { // PTE 为空则解除映射

        pte_unmap(vmf->pte);

        vmf->pte = NULL;

    }

}

 

关键点:

 

 若 PMD 未分配(pmd_none),暂不分配 PTE,避免干扰透明大页(THP)的并发操作。

 

 pmd_devmap_trans_unstable 处理透明大页分裂场景。

 

 

2. 处理 PTE 为空的情况(首次映射) 

 

if (!vmf->pte) {

    if (vma_is_anonymous(vmf->vma))

        return do_anonymous_page(vmf); // 匿名页映射

    else

        return do_fault(vmf); // 文件页/共享内存映射

}

 

static inline bool vma_is_anonymous(struct vm_area_struct *vma)

{

 return !vma->vm_ops;

}

 

分发逻辑:

 

 匿名页(vma->vm_ops == NULL):

 

  读操作:映射到零页(Zero Page)以减少物理内存占用。

 

  写操作:分配新物理页并初始化。

 

 文件页(vma->vm_ops != NULL):

 

  触发文件系统缺页处理(如 filemap_fault),从磁盘读取数据到 Page Cache

 

3. 处理 PTE 存在但 PRESENT 位为 0(已换出)

 

if (!pte_present(vmf->orig_pte))

    return do_swap_page(vmf); // 换回(Swap In)页面

 

原理:

PTE 存储了 Swap Entry(标识磁盘位置),需调用 do_swap_page 将数据从 Swap 分区读回物理内存

 

 

4. 处理 NUMA 迁移优化

 

if (pte_protnone(vmf->orig_pte) && vma_is_accessible(vmf->vma))

    return do_numa_page(vmf); // 迁移页面到当前 NUMA 节点

 

场景:

 

当物理页位于远端 NUMA 节点时,迁移以提升访问性能。

 

5. 写操作与写时复制(COW)

 

if (vmf->flags & FAULT_FLAG_WRITE) {

    if (!pte_write(entry))

        return do_wp_page(vmf); // 触发写时复制

    entry = pte_mkdirty(entry); // 标记脏页

}

 

COW 机制:

 

写只读页时,分配新物理页并复制内容,更新 PTE 指向新页(原页引用计数减 1)

 

若物理页仅被一个进程引用(无共享),则直接设为可写,避免复制。 

 

6. 更新 PTE 与 TLB 刷新

 

entry = pte_mkyoung(entry); // 标记访问位(PTE_AF)

if (ptep_set_access_flags(vma, address, pte, entry, write)) {

    update_mmu_cache(vma, address, pte); // 更新 CPU 缓存

} else if (write) {

    flush_tlb_fix_spurious_fault(vma, address); // 刷新 TLB 伪错误

}

 

关键操作:

 

ptep_set_access_flags:原子更新 PTE 的访问/脏位。

 

TLB 刷新仅在权限变更时触发(避免冗余刷新提升性能)。

 

 

三、处理流程图解

 

graph TD

    A[handle_pte_fault] --> B{PMD 有效?}

    B -- Yes --> C[获取 PTE]

    B -- No --> D[延迟 PTE 分配]

    C --> E{PTE 为空?}

    E -- Yes --> F{匿名页?}

    F -- Yes --> G[do_anonymous_page]

    F -- No --> H[do_fault]

    E -- No --> I{PTE Present?}

    I -- No --> J[do_swap_page]

    I -- Yes --> K{NUMA 迁移?}

    K -- Yes --> L[do_numa_page]

    K -- No --> M{写操作且只读?}

    M -- Yes --> N[do_wp_page]

    M -- No --> O[更新 PTE 标志]

    O --> P[刷新 TLB/缓存]

 

四、关键设计思想

 

延迟与优化:

 

 延迟 PTE 分配以避免透明大页冲突。

 

 零页映射节省匿名页首次读的内存

 

 

分层处理:

 

 按 PTE 状态(空/换出/写保护)分发给专用子函数,确保逻辑清晰。

 

并发控制:

 

 通过 spin_lock(vmf->ptl) 锁定页表,防止并行修改

 

性能优化:

 

 减少 TLB 刷新(仅在权限变更时触发)。

 

 区分 major/minor fault(是否涉及磁盘 I/O) 

 

五、典型场景与子函数对照表

 

场景 触发条件 处理函数 说明

 

匿名页首次访问 vmf->pte == NULL & 匿名 VMA do_anonymous_page 读:零页;写:分配新页

 

文件页首次映射 vmf->pte == NULL & 文件 VMA do_fault 读文件到 Page Cache

 

页面已换出 !pte_present(entry) do_swap_page 从 Swap 分区读回数据

 

写只读页(COW) write & !pte_write(entry) do_wp_page 复制页面或直接设可写

 

NUMA 优化迁移 pte_protnone(entry) do_numa_page 迁移页面至本地 NUMA 节点

 

页表更新 权限变更(如脏页/访问位) ptep_set_access_flags 更新 PTE 并刷新 TLB 

 

 

六、总结

 

handle_pte_fault 是 Linux 虚拟内存管理的核心枢纽,通过状态机式的分发逻辑处理各类缺页异常:

 

匿名/文件页:按需分配物理页或读取文件数据。

 

COW 机制:平衡内存共享与写操作性能。

 

Swap 与 NUMA:优化内存不足和跨节点访问场景。

其设计充分体现了 “懒加载”(Lazy Allocation)和 “最小化开销”(如零页、延迟刷新 TLB)的原则,确保高效管理复杂的内存访问需求。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93825.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93825.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于混合注意力网络和深度信念网络的鲁棒视频水印技术基础理论深度解析

1. 引言随着数字媒体技术的迅猛发展和互联网的普及,视频内容的创作、传播和分享变得前所未有的便捷。然而,这种便利性也带来了严重的版权保护挑战。数字视频的易复制性使得盗版和非法传播成为困扰内容创作者和版权所有者的重大问题。传统的加密技术虽然能…

linux 之virtio 的驱动框架

1、基本知识 上一篇文章介绍了 virtio 的核心数据的实现和逻辑:linux 之 virtio 子系统核心的数据结构-CSDN博客 virtio 是对半虚拟化 hypervisor 中的一组通用模拟设备的抽象。它允许 hypervisor 导出一组通用的模拟设备,并通过一个通用的应用编程接口…

项目1总结其三(图片上传功能)

1、UploadService public interface UploadService {//上传图片String uploadImage(MultipartFile file, String type); }upload.location D:/upload Value("${upload.location}")private String uploadLocation;//文件上传路径Overridepublic String uploadImage(M…

Linux应用层开发--线程池介绍

Glib 线程池 1. 线程池简介 线程池是一种管理和重用多个线程的设计模式: 避免频繁创建/销毁线程的开销。提高性能与资源利用率。任务提交后,由线程池内的线程自动执行,任务执行完线程不会退出,而是继续等待下一个任务。 2. Gli…

【Python】Python 多进程与多线程:从原理到实践

Python 多进程与多线程:从原理到实践 文章目录Python 多进程与多线程:从原理到实践前言一、并发编程基础:进程与线程1.1 进程(Process)1.2 线程(Thread)1.3 进程与线程的关系二、Python 中的 &q…

electron-vite_18Less和Sass共用样式指定

项目中可以封装less公用样式和方法&#xff0c;比如自动以滚动条样式、单行省略号、多行省略号、display:none等&#xff1b;关于additionalData的配置生效,请在main.js中引入一个别的样式或vue组件中使用“<style lang“scss”><style>”找到electron.vite.config…

Python面试题及详细答案150道(71-80) -- 文件操作篇

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

python新工具-uv包管理工具

uv 是一个由 Astral (Ruff 的创建者) 开发的极速 Python 包和项目管理器&#xff0c;用 Rust 编写。它旨在作为传统 Python 包管理工具&#xff08;如 pip、pip-tools、pipx、poetry、pyenv、twine 和 virtualenv 等&#xff09;的替代品&#xff0c;通过其高性能和多功能集成&…

有关spring-ai的defaultSystem与systemMessage优先级

今天在写项目的时候想用nacos随时修改system的prompt&#xff0c;突然发现defaultSystem的优先级比systemMessage高很多&#xff0c;废话我就不说了&#xff0c;看图吧。你觉得证据不够&#xff1f;那这样呢&#xff1f;

#运维 | 前端 # Linux http.server 实践:隐藏长文件名,简短路径 (http://IP:port/别名 ) 访问

如何运行页面为 http://ip:port/名称 1. 准备文件目录 假设文件原始位置&#xff1a; /home/ubuntu/projects/yinran/ckd.html将它移动到子目录并改名为 index.html&#xff1a; mkdir -p /home/ubuntu/projects/yinran/ckd mv /home/ubuntu/projects/yinran/ckd.html \/home/u…

任务管理器不刷新

记录一个小问题&#xff1a; 进入任务管理器之后发现页面不会刷新&#xff0c;性能界面也是一致。解决办法&#xff1a;查看–>更新速度–>正常

2025-08-21 Python进阶9——__main__与lambda

文章目录1 \_\_main\_\_1.1 name 变量1.1.1 当模块作为主程序直接运行时1.1.2 当模块被其他模块导入时1.2 \_\_main\_\_ 的含义1.3 if \_\_name\_\_ \_\_main\_\_1.5 小结2 lambda表达式2.1 基本概念2.2 lambda 函数语法2.3 使用示例2.4 与高阶函数结合使用2.4.1 与 map () 结…

Java:将视频上传到腾讯云并通过腾讯云点播播放

功能需求:传入一个videoFile也就是视频字节流,返回腾讯云点播的视频保存url需要在腾讯云中寻找的配置信息:导入的依赖:<!--腾讯云点播--><dependency><groupId>com.tencentcloudapi</groupId><artifactId>tencentcloud-sdk-java</artifactId&…

Unity3D物理游戏网络同步指南

前言 Unity3D 物理游戏的网络同步是一个复杂但非常核心的话题。要实现一个流畅、公平且可扩展的多人物理游戏&#xff0c;需要深入的理解和精心的设计。 下面我将为你全面解析 Unity3D 物理游戏的网络同步&#xff0c;包括核心概念、主流方案、实现细节以及最佳实践。 对惹&…

Amazon Redshift 访问配置完整指南

概述 Amazon Redshift 是 AWS 提供的云端数据仓库服务,支持多种访问方式。本文将详细介绍如何配置 IAM 权限、使用 AWS 控制台 Query Editor v2,以及通过 SQL Workbench/J 等第三方工具连接 Redshift 集群。 目录 环境准备 IAM 权限配置 Redshift 用户管理 AWS 控制台访问 …

electron-vite_19配置环境变量

前端配罟环境变量主要通过项目根目录下的.env系列文件实现&#xff0c;不同框架(如Vue、React)或构建工具(如Vite、Webpack)的具体操作略有差异&#xff0c;但核心逻辑均为通过环境变量文件区分开发、测试、生产等环境。方案1: 直接在根目录新建.env文件 1.在根目录新建 .env.d…

【python】arange用法

1. NumPy 里的 np.arangeimport numpy as np# 语法 np.arange([start, ]stop, [step, ], dtypeNone)参数说明&#xff1a;start&#xff1a;起始值&#xff08;默认 0&#xff09;stop&#xff1a;终止值&#xff08;不包含这个值&#xff09;step&#xff1a;步长&#xff08;…

力扣1005:k次取反后最大化的数组和

力扣1005:k次取反后最大化的数组和题目思路代码题目 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&…

国产数据库管理工具 CloudDM 2.7.1.0 发布,OceanBase 等数据源支持复杂 SQL 脱敏数据

CloudDM 是 ClouGence 公司推出的面向团队使用的数据库管理工具&#xff0c;支持云上、云下、多云等多种环境&#xff0c;并且提供多达 23 种数据源的支持。CloudDM 还支持数据库 DevOps CI/CD 功能&#xff0c;将用户产品发布流程中数据库发布和程序发布无缝串联起来。 更新亮…

AI大模型实战:用自然语言处理技术高效处理日常琐事

引言在数字化时代&#xff0c;我们每天都会面对大量的琐碎事务&#xff1a;整理会议记录、处理名单数据、撰写学习笔记等等。这些工作不仅耗时&#xff0c;而且容易出错。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;特别是大语言模型&#xff08;LLM&#xff09;的…