虚拟内存与缺页机制与实现详解

虚拟内存与缺页机制与实现详解

源码路径rk3588/kernel-6.1/mm/memory.cmmap.cmprotect.cmremap.cmlock.cmadvise.c
内核版本:Linux 6.1(RK3588 / ARM64)
文档目录linuxDoc/mm/


目录


一、原理

1.1 虚拟地址空间

每个用户进程有 struct mm_struct,包含:

  • 页表根(pgd
  • VMA 链表 / maple treevm_area_struct):每段连续虚拟区间的权限、文件映射、堆栈属性
  • mmap_lock:保护 VMA 与页表并发

CPU 通过 MMU 将虚拟地址译为物理地址;页表项无效或访问违例触发 缺页异常

1.2 按需分配(Demand Paging)

匿名映射、文件映射通常 先建立 VMA,不立即分配物理页;首次访问触发 fault:

  • 匿名:分配零页或 COW 副本
  • 文件:从 page cache 读入或新建 cache 页
  • swap:从 swap 读回

1.3 写时复制(COW)

fork() 后父子共享只读 PTE;任一方写入触发 写保护 faultdo_wp_page() 分配新物理页并更新 PTE。

1.4 大页与 THP

若 VMA 允许且策略开启,可在 PMD/PUD 层安装 透明大页create_huge_pmd 等),减少 TLB miss。


二、实现方式

2.1 mmap:do_mmap()

mmap.cdo_mmap()(~1253 行):

  1. get_unmapped_area() 找空洞
  2. vm_mmap_pgoff()do_mmap_pgoff
  3. 创建 vm_area_struct,设置 vm_opsvm_flagsVM_READ/WRITE/EXECVM_SHARED 等)
  4. 文件映射调用 file->f_op->mmapgeneric_file_mmap
  5. 插入 maple tree / 链表,必要时 userfaultfd 通知

munmap / expand_stack / brk 在同文件修改或删除 VMA。

2.2 缺页总入口

架构层(arch/arm64/mm/fault.c)解析异常后调用:

handle_mm_fault(vma, address, flags, regs)__handle_mm_fault()

1
2
3
4
5
6
7
8
static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
// 初始化 vm_fault vmf
pgd → p4d → pud → pmd 逐级分配/检查
// THP: create_huge_pud / wp_huge_pud / huge_pmd_set_accessed
return handle_pte_fault(&vmf);
}

2.3 PTE 级:handle_pte_fault()

根据 PTE 状态分支(同文件):

情况 处理
PTE 不存在 do_anonymous_page / 文件 fault
PTE 仅读 + 写访问 do_wp_page(COW)
swap PTE do_swap_page
NUMA hint fault do_numa_page
设备/特殊 vm_ops->fault

返回 vm_fault_tVM_FAULT_OKVM_FAULT_RETRY(可能释放 mmap_lock)、VM_FAULT_OOMVM_FAULT_SIGSEGV 等。

2.4 匿名页:do_anonymous_page

  • alloc_zeroed_user_highpage_movable 或类似分配
  • alloc_set_pte 安装 PTE
  • memcg charge(若启用)

2.5 写保护:do_wp_page

  • 若仍共享:分配新页,copy_page / copy_present_page
  • 更新 PTE 为可写
  • userfaultfdKSMTHP 有交叉分支

2.6 相关系统调用实现

文件 功能
mprotect.c 修改 VMA 权限,刷新 PTE 权限位
mremap.c 移动/扩展映射,可能 move_page_tables
mlock.c mlock 使页常驻,munlock 恢复
madvise.c MADV_FREE/DONTNEED/HUGEPAGE
mincore.c 查询页是否在内存
msync.c 映射区与文件同步

2.7 mmu_gather

批量 unmap/munmap 时用 struct mmu_gathermmu_gather.c)累积 TLB flush,减少 IPI。


三、关键数据结构与接口

结构 说明
struct mm_struct 进程地址空间
struct vm_area_struct 虚拟区段
struct vm_fault fault 上下文(vma、addr、pgoff、gfp)
handle_mm_fault 缺页主入口
do_mmap 建立映射

mmap_read_lock / mmap_write_lock;fault 中可能 VM_FAULT_RETRY 释放锁后重试。


四、RK3588 平台说明

  • ARM64 使用 4 级页表(PGD/P4D/PUD/PMD/PTE,P4D 可能折叠)。
  • 编解码进程大量 匿名映射 + mmap(dma-buf fd):文件类 fault 在 dmabuf/file 路径,匿名堆栈在 do_anonymous_page
  • MADV_DONTNEED 可释放冷数据缓解内存压力(与 07 回收协同)。

五、调试与观测

手段 说明
/proc/<pid>/maps VMA 列表
show_vma / procfs 内核打印 VMA
handle_mm_fault trace mm tracepoints
userfaultfd 用户态处理 fault

附录:源码索引

主题 文件 约略行号
__handle_mm_fault memory.c 5079
handle_mm_fault memory.c 5262
do_mmap mmap.c 1253
mmu_gather mmu_gather.c 全文

文章互动

阅读 --

留言

0 条留言

正在加载留言…