#Linux内存管理#缺页中断处理的核心函数是do_page_fault()的工作原理

深入剖析 Linux 内核 do_page_fault() 函数的工作原理

do_page_fault() 是 Linux 内核中处理缺页中断的核心函数,它是 ARMv7-A(及 x86 等)架构下虚拟内存管理的关键枢纽。这个函数负责响应硬件缺页异常,判断错误类型,并执行相应的内存管理操作。

函数原型与关键参数:

void do_page_fault(struct pt_regs *regs, unsigned int esr);

regs: 指向进程在内核态保存的用户态寄存器上下文(包含触发缺页时的 PC、SP 等所有通用寄存器)

esr (Exception Syndrome Register): ARMv7-A 中的 IFSR 寄存器值,包含故障的详细原因代码

处理流程详解(针对 ARMv7-A)

第一步:获取关键硬件信息

unsigned long address = read_cp15_DFAR(); // 获取故障地址 (DFAR)

unsigned int fsr = read_cp15_IFSR(); // 获取故障状态 (IFSR)

address: 导致缺页的虚拟地址 (来自 DFAR - Data Fault Address Register)

fsr: 故障状态码 (来自 IFSR - Instruction Fault Status Register),包含:

FSR[3:0]: 故障类型 (如 0x5=Translation fault, 0x7=Access flag fault)

FSR[5:4]: 域 (Domain)

FSR[6]: 写操作标识 (1=写操作触发)

FSR[10]: 指令中止标记 (0=数据访问)

第二步:检查内核空间缺页

if (unlikely(address >= TASK_SIZE)) {

// 在内核空间发生缺页

if (!user_mode(regs)) {

// 内核自身导致的缺页 (可能严重错误)

vmalloc_fault(address); // 处理内核动态映射

return;

}

// 用户进程错误访问内核空间

bad_area(regs, address);

return;

}

内核态缺页: 处理 vmalloc 动态映射的延迟分配

用户访问内核空间: 立即终止进程 (SIGSEGV)

第三步:查找对应的 VMA (Virtual Memory Area)

vma = find_vma(mm, address);

if (unlikely(!vma)) {

// 虚拟地址不存在于任何 VMA 中

bad_area(regs, address);

return;

}

if (likely(vma->vm_start <= address))

goto good_area; // 成功落在 VMA 范围内

遍历进程的 mm_struct->mmap 链表查找包含该地址的 VMA

未找到说明是非法访问 (SIGSEGV)

第四步:权限检查 (good_area)

good_area:

write = (fsr & FSR_WRITE) != 0;

exec = (fsr & FSR_EXEC) != 0;

// 检查 VMA 权限是否匹配

if (unlikely(access_error(fsr, vma))) {

// 没有权限访问此区域 (如写只读区)

bad_area_access_error(regs, fsr, address, vma);

return;

}

关键检查项:

请求类型 VMA 权限要求 可能处理方式

READ VM_READ 正常处理

WRITE VM_WRITE 正常处理或 COW

EXECUTE VM_EXEC 正常处理或 SIGSEGV

WRITE+无VM_SHARED VM_WRITE COW 处理

第五步:区分多种缺页类型

switch (fsr & 0xF) { // 处理 FSR 的故障类型位

案例 1:首次访问的匿名页(最常见情况)

if (handle_mm_fault(vma, address,

write ? FAULT_FLAG_WRITE : 0) &

VM_FAULT_ERROR) {

// 处理分配失败

oom_kill();

}

触发场景:malloc() 后首次写入内存

操作:调用 handle_mm_fault()

分配清零的物理页 (通过 alloc_page())

建立页表映射

设置 PTE 的 PTE_VALID | PTE_USER | PTE_WRITE

案例 2:写时复制 (Copy-On-Write)

if (write && !(vma->vm_flags & VM_SHARED)) {

if (!(vma->vm_flags & VM_WRITE)) {

// 尝试写入私有只读页 (COW触发点)

handle_cow_fault(vma, address);

}

}

触发场景:写入共享库文本段或 fork() 后的父子共享页

操作:

分配新物理页

复制原始页内容 (copy_pte_range())

将进程 PTE 改为指向新副本并标记可写

案例 3:文件支持的页缺页

if (vma->vm_ops && vma->vm_ops->fault) {

ret = vma->vm_ops->fault(vma, address,

write ? FAULT_FLAG_WRITE : 0);

}

触发场景:首次访问 mmap 映射的文件区域

操作:

调用文件系统的 filemap_fault()

从磁盘读取数据到新页

更新页表指向缓存页

案例 4:换出页的换入

if (pte_swp_swapin_pte(orig_pte)) {

swap_in_page(address, entry);

}

触发场景:访问已被换出到交换区的页面

操作:

分配新物理页

从 swap 分区读回数据

重建页表项并清除 swap 标记

案例 5:访问权限位缺失

if ((fsr & 0xF) == 0x7) { // Access Flag Fault

handle_pte_fault(..., FAULT_FLAG_ACCESS);

}

ARMv7-A 特有:当 L2 PTE 存在但访问位(APX)未设置时触发

操作:仅设置 PTE 的访问位,不需分配物理页

第六步:更新页表和刷新 TLB

flush_tlb_page(vma, address); // 使旧TLB项失效

关键操作:使用协处理器指令 (CP15)

mcr p15, 0, <addr>, c8, c7, 1 (TLBIMVA - 使单个条目失效)

mcr p15, 0, <domain>, c8, c7, 0 (TLBIALL - 全无效)

第七步:错误恢复路径

return; // 正常返回将重执行失败指令

bad_area:

force_sig_fault(SIGSEGV, SEGV_MAPERR, ...);

return;

oom:

pagefault_out_of_memory(); // 触发OOM killer

return;

非法访问:发送 SIGSEGV 信号终止进程

内存不足:调用 OOM killer 选择牺牲进程

ARMv7-A 的特有处理考量

1.L1/L2 转换错误区分:

if (fsr & FSR_L1_PF) handle_l1_translation_fault();

else handle_l2_translation_fault(); // 最常见的缺页情况

2.域访问控制处理:

domain = (fsr >> 4) & 0x0F;

if (domain != DOMAIN_USER) {

// 内核域访问错误处理

}

3.精确的异常类型判断:

FSR[3:0] 错误类型 处理方式

0b0000 Alignment SIGBUS 信号

0b0101 Section trans 一级页表错误处理

0b0111 Page trans 二级页表错误 (主要缺页)

0b1001 Domain section 域权限错误

性能关键路径优化

__do_page_fault():

if (kmem_cache_page_fast_alloc()) // 快速路径分配

goto fast_path;

spin_lock(&mm->page_table_lock); // 慢速路径加锁

// ...详细处理

spin_unlock(&mm->page_table_lock);

do_page_fault() 通过解析硬件状态码,结合进程内存描述,智能地处理了 10+ 种不同类型的页面错误。它不仅是物理内存分配的入口,更是实现写时复制、内存映射文件、交换机制等高级功能的统一网关。对缺页路径的极致优化直接决定了操作系统整体的内存访问性能。

相关推荐
心一信息1 小时前
如何在Ubuntu上部署excalidraw
linux·运维·ubuntu
人生匆匆1 小时前
linux ext4缩容home,扩容根目录
linux·运维·服务器
yuanzhengme2 小时前
Shell【脚本 02】离线安装配置Zookeeper及Kafka并添加service服务和开机启动(脚本分析)
linux·zookeeper·kafka·自动化·安装脚本
Johny_Zhao3 小时前
阿里云平台健康检查巡检清单-运维篇
linux·网络安全·阿里云·信息安全·云计算·shell·系统运维
来自于狂人3 小时前
CentOS 镜像源配置与 EOL 后的应对策略
linux·运维·centos
博语小屋4 小时前
进程初识之进程状态
linux
吉凶以情迁5 小时前
window服务相关问题探索 go语言服务开发探索调试
linux·服务器·开发语言·网络·golang
柏木乃一6 小时前
Linux初步认识与指令与权限
linux·运维·服务器·shell·权限
Joemt6 小时前
ubuntu源码编译安装cmake高版本、pybind11安装、crow使用
linux·运维·ubuntu