[linux内存原理] kswap 📅 2026/7/9 6:12:41 回收策略1 . 文件页 (File-backed Pages)干净页 (Clean Pages)*内存里的数据和磁盘文件里的一模一样。处理方式直接丢弃因为磁盘上已经有一份一模一样的了下次需要时再读进来就行。这是最快、最常用的回收方式。脏页 (Dirty Pages)*你在内存里修改了数据但还没同步到磁盘文件例如你保存文档前的编辑处理方式写入磁盘对应的文件中。内核会调用文件系统的写入接口把这 4KB 数据同步到它原来所属的那个 .dat 或 .txt 文件里。同步完后它就变成了“干净页”可以被回收。匿名页 (Anonymous Pages) 这类页面没有对应的磁盘文件是进程运行产生的临时数据比如程序的堆、栈、全局变量。处理方式写入 Swap交换空间内核通过 vm.swappiness 参数0-200默认 60来平衡这两者的回收比例接近 0极力避免回收匿名页优先清理文件缓存可能会让程序运行变快但可用内存少。接近 200极其激进地进行 Swap哪怕文件缓存还很多。Q cgroup.swap.max对swap进行限额是怎么做的A当系统感知到内存压力如达到低水位并触发回收直到最终检查到 swap.max 的完整调用栈如下。我们可以分为两个阶段触发阶段谁发起的回收和 执行阶段如何执行回收并碰到 Swap 限制。阶段一触发回收 (Reclaim Trigger) 有两种主要的触发路径路径 A异步回收 (kswapd)当系统物理内存低于 WMARK_LOW 时内核线程 kswapd 被唤醒。kswapd()└─balance_pgdat()// 负载均衡尝试平衡内存节点└─mem_cgroup_soft_reclaim()// (可选) 针对软限制回收└─shrink_node()// 核心节点扫描函数路径 B直接回收 (Direct Reclaim)当进程申请内存如 malloc 后的第一次写操作且系统内存严重不足低于 WMARK_MIN时分配路径会阻塞并主动回收。handle_mm_fault()└─__alloc_pages()└─__alloc_pages_slowpath()└─__alloc_pages_direct_reclaim()└─try_to_free_pages()└─do_try_to_free_pages()└─shrink_node()// 进入核心扫描逻辑阶段二执行回收与 Swap 限制检查一旦进入 shrink_node内核会遍历 LRU 链表并尝试释放页面shrink_node() └─ shrink_node_memcgs() // 遍历该节点下的所有 memcgs └─ shrink_lruvec() // 扫描特定 cgroup 的 LRU 向量 └─ shrink_list() └─ shrink_folio_list() // 扫描一组页面 (Folio) 的核心函数在 shrink_folio_list() 内部位于 mm/vmscan.c对于匿名页的处理逻辑如下shrink_folio_list()│ ├─// 1. 发现是匿名页 (Anonymous Folio)├─folio_test_anon(folio)│ ├─// 2. 尝试为该页面分配 Swap 空间├─add_to_swap(folio)// 位于 mm/swap_state.c│ └─folio_alloc_swap(folio)// 位于 mm/swap_slots.c│ └─get_swap_pages()// 申请物理磁盘上的 swap slot│ └─mem_cgroup_try_charge_swap()// 检查 Cgroup 额度! (mm/memcontrol.c)│ └─page_counter_try_charge(memcg-swap,...)│ └─// 如果 count swap.max返回 -ENOMEM│ ├─// 3. 如果 add_to_swap() 因为 -ENOMEM 返回失败└─ activate_locked:└─// 页面被重新放回活跃 LRU 链表回收宣告失败page_counter_try_charge 会对swap.max做判断如果超额返回错误