土法炼钢兴趣小组的算法知识备份

【操作系统百科】缺页处理

文章导航

分类入口
os
标签入口
#pagefault#cow#userfaultfd#major-fault#minor-fault

目录

缺页是 VM 按需分配的引擎:进程访问 VA → PTE 不 present / 权限不符 → CPU 发 #PF 异常 → 内核进 fault handler → 分配/修复 → 进程无感继续。

一、先看图

flowchart TD
    ACC[用户访问 VA] --> PF[#PF 异常]
    PF --> DPF[do_page_fault]
    DPF --> VMA{找到 VMA?}
    VMA -->|否| SEGV[SIGSEGV]
    VMA -->|是| PERM{权限匹配?}
    PERM -->|写只读非 COW| SEGV
    PERM -->|OK| HMM[handle_mm_fault]
    HMM --> WALK[walk 页表]
    WALK --> PTE{PTE 状态}
    PTE -->|none: 首次访问| ALLOC[alloc page<br/>minor fault]
    PTE -->|file: 需读盘| FILEIO[read page<br/>major fault]
    PTE -->|swap entry| SWAPIN[swap in<br/>major fault]
    PTE -->|present + RO + COW| COWBRK[COW break<br/>minor fault]
    PTE -->|uffd 注册| UFFD[userfaultfd<br/>通知用户态]
    classDef ok fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    classDef err fill:#f8514922,stroke:#f85149,color:#adbac7;
    classDef io fill:#f0883e22,stroke:#f0883e,color:#adbac7;
    class ALLOC,COWBRK ok
    class SEGV err
    class FILEIO,SWAPIN,UFFD io

二、五种缺页

2.1 Minor fault(软缺页)

PTE 为空但物理页可在内存里立刻分配/找到。不涉及磁盘 I/O。

场景:匿名页首次写、page cache 已驻留的文件页首次映射。

2.2 Major fault(硬缺页)

需要磁盘 I/O:文件页不在 cache 要读盘,或匿名页已 swap-out 要 swap-in。

开销可达 ms 级(HDD 10+ms,SSD 0.1ms)。

2.3 COW fault

fork 后父子共享页标只读。任一方写 → #PF → 检测 COW → 分配新页、复制内容、改 PTE → 写方拿私有。

2.4 Swap fault

PTE 里存的是 swap entry(设备+偏移)而非物理地址。先查 swap cache,miss 就从 swap 读回。

2.5 Userfaultfd

用户态注册了 userfaultfd → 缺页不由内核处理,而是通知用户态程序填充页。用于 CRIU(热迁移)、live migration、postcopy。

三、do_page_fault 调用链

CPU #PF → exc_page_fault()
  → do_user_addr_fault() / do_kern_addr_fault()
    → find_vma()         // 定位 VMA
    → check_access()     // 权限检查
    → handle_mm_fault()  // 核心入口
      → __handle_mm_fault()
        → walk 到 PTE 层
          → handle_pte_fault()
            → do_anonymous_page()    // anon 首次
            → do_fault()             // file-backed
            → do_swap_page()         // swap
            → do_wp_page()           // COW write-protect

四、COW 详解

COW break 关键路径:

  1. do_wp_page 检测 page_count(page) > 1(多进程共享)
  2. 分配新页 alloc_page(GFP_HIGHUSER_MOVABLE)
  3. copy_user_highpage(new, old, addr, vma)
  4. 更新 PTE:新页 + 可写
  5. page_remove_rmap(old) → 老页引用减一
  6. flush TLB

fork+exec 快的秘密:exec 会 exit_mmap 丢弃所有 VMA,绝大部分页根本不会被 COW。

五、file-backed fault

文件映射缺页走 vm_ops->fault

ext4_file_fault → filemap_fault
  → find_get_page(mapping, offset)  // 在 page cache 查
  → 未命中 → page_cache_sync_readahead → submit_bio
  → 等 I/O 完成 → 设 PTE

readahead 机制预读相邻页,减少后续 major fault。

六、hugefault

THP 或 HugeTLB 缺页:

THP 的 COW 要复制 2MB,延迟更高;所以数据库倾向 madvise 模式(仅明确标记的区域用 THP)。

七、userfaultfd

int uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK);
struct uffdio_api api = {.api = UFFD_API};
ioctl(uffd, UFFDIO_API, &api);

struct uffdio_register reg = {
    .range = {.start=addr, .len=len},
    .mode = UFFDIO_REGISTER_MODE_MISSING,
};
ioctl(uffd, UFFDIO_REGISTER, &reg);

// 用户态 poll(uffd) → 收到 UFFD_EVENT_PAGEFAULT
// → UFFDIO_COPY 或 UFFDIO_ZEROPAGE 填充

应用场景:

八、fault 计数与观测

# 进程级
ps -o min_flt,maj_flt -p $$

# 系统级
sar -B 1
# pgfault/s, pgmajfault/s

# 实时追踪
perf stat -e page-faults,major-faults ./app

# 精细追踪
bpftrace -e 'tracepoint:exceptions:page_fault_user { @[comm]=count(); }'

major fault 高意味着要么 page cache 不够,要么 swap 活跃——查 /proc/meminfo

九、缺页优化

9.1 MAP_POPULATE

提前把所有页分配好,避免运行时 fault 抖动:

mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);

9.2 madvise(MADV_WILLNEED)

文件映射:提前 readahead。比 MAP_POPULATE 更灵活(可按区域)。

9.3 mlock

锁定后不会被换出,就不会有 swap fault:

mlock(addr, size);

9.4 huge page

减少 fault 次数(一次 fault 填 2MB 而非 4KB)。

9.5 prefault

内核 mm_populate 在 mmap 返回前批量 fault,用于 MAP_POPULATE。

十、SIGSEGV 路径

VMA 找不到、或权限检查失败、或 stack guard 越界 → SIGSEGV

常见场景:

core dump 文件会记录 fault 地址和 si_code(SEGV_MAPERR / SEGV_ACCERR)。

十一、小结


参考文献

工具


上一篇mm_struct 与 VMA 下一篇页缓存深入

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-20 · os

【操作系统百科】虚拟内存模型

虚拟内存是现代 OS 最核心的抽象:每个进程都像独占一块连续大内存。本文讲 VM 到底给了什么、代价是什么——VA/PA 映射、保护、隔离、COW、mmap 语义、overcommit、地址空间布局。

2026-04-27 · os

【操作系统百科】内存回收

Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。

2026-04-28 · os

【操作系统百科】交换

swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。

2026-05-03 · os

【操作系统百科】Slab/SLUB 分配器

buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。


By .