虚拟内存与缺页机制与实现详解
源码路径:
rk3588/kernel-6.1/mm/memory.c、mmap.c、mprotect.c、mremap.c、mlock.c、madvise.c
内核版本:Linux 6.1(RK3588 / ARM64)
文档目录:linuxDoc/mm/
目录
一、原理
1.1 虚拟地址空间
每个用户进程有 struct mm_struct,包含:
- 页表根(
pgd) - VMA 链表 / maple tree(
vm_area_struct):每段连续虚拟区间的权限、文件映射、堆栈属性 mmap_lock:保护 VMA 与页表并发
CPU 通过 MMU 将虚拟地址译为物理地址;页表项无效或访问违例触发 缺页异常。
1.2 按需分配(Demand Paging)
匿名映射、文件映射通常 先建立 VMA,不立即分配物理页;首次访问触发 fault:
- 匿名:分配零页或 COW 副本
- 文件:从 page cache 读入或新建 cache 页
- swap:从 swap 读回
1.3 写时复制(COW)
fork() 后父子共享只读 PTE;任一方写入触发 写保护 fault → do_wp_page() 分配新物理页并更新 PTE。
1.4 大页与 THP
若 VMA 允许且策略开启,可在 PMD/PUD 层安装 透明大页(create_huge_pmd 等),减少 TLB miss。
二、实现方式
2.1 mmap:do_mmap()
mmap.c 中 do_mmap()(~1253 行):
get_unmapped_area()找空洞vm_mmap_pgoff()→do_mmap_pgoff- 创建
vm_area_struct,设置vm_ops、vm_flags(VM_READ/WRITE/EXEC、VM_SHARED等) - 文件映射调用
file->f_op->mmap或generic_file_mmap - 插入 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 | static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma, |
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_t:VM_FAULT_OK、VM_FAULT_RETRY(可能释放 mmap_lock)、VM_FAULT_OOM、VM_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 为可写
- 与 userfaultfd、KSM、THP 有交叉分支
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_gather(mmu_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 | 全文 |
正在加载留言…