长文106: 内存管理-高级篇:写时复制、交换与 NUMA 优化深度解析

“当物理内存不足时,操作系统如何优雅地处理? 本文将深入写时复制(CoW)、交换(Swapping)、NUMA 优化三大高级特性, 并对比 Linux 的工业级实现,构建一个完整的内存过载处理框架。”

引言:内存过载的挑战与解决方案

现代操作系统面临的核心挑战是: 物理内存有限,但应用内存需求无限 。当系统内存压力增大时,内核必须:

高效 fork 进程

:避免复制大量内存页
优雅处理内存不足

:通过交换释放物理内存
优化多核 NUMA 系统

:减少跨节点访问延迟

三大高级内存特性正是为解决这些问题而生:

写时复制

(CoW):
fork
时共享页,写时才复制
交换

(Swapping):将不活跃页换出到磁盘
NUMA 感知

:本地节点优先分配

本文将系统性地剖析这些特性,从算法原理到 Linux 实现,并提供可运行的工业级框架。


第一章:写时复制(Copy-on-Write, CoW)

1.1 CoW 的核心价值

fork 的性能问题:

传统 fork

:复制父进程所有页 → 内存/时间开销巨大
实际场景


fork
后通常立即
exec
,复制的页被浪费

CoW 解决方案:

fork 时共享页

:父子进程共享物理页
只读保护

:页表项 R/W=0
写时复制

:Page Fault 时复制页

性能提升:

fork 速度提升 10-100 倍
内存节省 50%+

(共享库、只读数据)

1.2 CoW 实现原理

fork 时的页表操作

Page Fault 处理 CoW

1.3 Linux CoW 实现细节

引用计数管理

Linux 使用 页描述符 ( struct page )跟踪引用:

页表项标志

Linux 使用 特殊标志 标记 CoW 页:

_PAGE_DIRTY

:写时置 1,但 CoW 页初始为 0
_PAGE_RW

:0 表示只读(可能 CoW)

优化:零页(Zero Page)

只读零页

:共享的全零页
节省内存

:未写入的 CoW 页指向零页


第二章:交换(Swapping)机制

2.1 交换的核心价值

物理内存不足的后果:

OOM Killer

:强制杀死进程
系统卡死

:无法分配关键内存

交换解决方案:

交换区

(Swap Partition/File):磁盘上的内存扩展
页面换出

:将不活跃页写入交换区
页面换入

:访问时从交换区读回

优势:

避免 OOM

:内存不足时优雅降级
支持大内存应用

:虚拟内存 > 物理内存

2.2 交换区管理

交换区数据结构

交换区初始化

2.3 页面换出机制

页面换出流程

  1. 内存回收触发

    :水位线低于 low

  2. 选择不活跃页

    :LRU 算法

  3. 写入交换区

    :分配交换槽,写磁盘

  4. 更新页表

    :标记为交换页

交换槽分配

页表项标记交换页

2.4 页面换入机制

Page Fault 处理交换页

2.5 Linux 交换实现细节

LRU 链表

Linux 使用 双 LRU 链表 管理页:

活跃链表

(Active):频繁访问的页
不活跃链表

(Inactive):候选换出页

页面回收策略

kswapd

:后台回收,维持水位线
直接回收

:分配路径触发
内存压缩

(zswap):压缩页而非换出


第三章:NUMA 感知内存管理

3.1 NUMA 架构挑战

NUMA 基础:

本地节点

:CPU 访问本地内存延迟低
远程节点

:跨节点访问延迟高(2-3 倍)

问题:

随机分配

:50% 内存访问跨节点
性能下降

:应用性能降低 20-50%

3.2 NUMA 内存分配策略

基本策略:

本地优先

:优先分配本地节点内存
轮询分配

:多节点间轮询(避免单节点耗尽)
绑定分配

:强制指定节点(
MPOL_BIND

Linux NUMA 策略:

3.3 NUMA 数据结构

节点内存描述符

内存策略

3.4 NUMA 分配实现

节点选择

交错分配(Interleave)

3.5 页面迁移(Page Migration)

迁移场景:

NUMA 平衡

:将远程页迁移到本地
内存碎片整理

:合并大块内存

迁移流程:

  1. 分配新页

    :本地节点

  2. 复制内容

    :原页 → 新页

  3. 更新映射

    :所有进程页表

  4. 释放原页

Linux 实现:


第四章:内存压缩(zswap)简介

4.1 zswap 的核心思想

传统交换问题:

磁盘 I/O 慢

:换出/换入延迟高
SSD 寿命

:频繁写入损耗

zswap 解决方案:

压缩页存储在内存

:避免磁盘 I/O
仅当压缩池满时换出

:减少磁盘访问

工作流程:

  1. 页面换出

    :压缩页存入 zswap 池

  2. 池满时

    :换出最旧压缩页到交换区

  3. 页面换入

    :从 zswap 池解压

4.2 zswap 数据结构

压缩池

压缩页项

4.3 zswap 性能优势

指标 传统交换 zswap
换出延迟 10-100ms 0.1-1ms
换入延迟 10-100ms 0.1-1ms
SSD 寿命
内存开销 压缩池

💡 zswap 适合内存充足但磁盘慢的场景 (如笔记本、虚拟机)


第五章:Linux 高级内存特性对比

5.1 CoW 实现对比

特性 简化实现 Linux
引用计数 struct page->_refcount
零页优化 共享 zero_page
页表标志 手动管理 _PAGE_DIRTY 自动跟踪
大页 CoW 不支持 支持 THP CoW

5.2 交换实现对比

特性 简化实现 Linux
LRU 算法 双 LRU 链表
交换类型 仅匿名页 匿名页 + 文件页
压缩 zswap/zram
多交换区 支持 支持,按优先级

5.3 NUMA 实现对比

特性 简化实现 Linux
策略类型 仅本地优先 MPOL_DEFAULT/INTERLEAVE/BIND
页面迁移 支持 numa_migrate_pages
自动平衡 numa_balancing 内核线程
CPU 绑定 cpuset 支持

结论:高级内存管理的系统工程

高级内存特性是操作系统内核的 系统工程典范 ,它们融合了:

算法优化

(CoW、LRU、NUMA 策略)
硬件特性

(TLB、NUMA 拓扑)
性能工程

(zswap 压缩、页面迁移)
用户体验

(避免 OOM、平滑降级)

理解这些特性不仅有助于内核开发,更能培养 全栈系统思维 :

资源约束

:物理内存有限,需智能管理
性能权衡

:内存 vs 磁盘 vs CPU
架构演进

:从单核到 NUMA,从交换到压缩

对于希望深入 Linux 内核的开发者,建议:

阅读
mm/memory.c

mm/swap.c

mm/mempolicy.c
使用
numastat

vmstat
监控内存行为
通过
perf
分析 Page Fault 和交换开销

高级内存管理的故事,仍在继续。随着 CXL、持久内存等新硬件的出现,这一复杂而优雅的系统将面临新的挑战与机遇。


附录:关键数据结构与函数速查

核心数据结构

结构 作用 文件
struct page 页描述符 mm_types.h
struct swap_info_struct 交换区 swap.h
pg_data_t NUMA 节点 mmzone.h
struct mempolicy 内存策略 mempolicy.h

关键函数

函数 功能 文件
do_cow_fault CoW 处理 memory.c
shrink_lruvec 页面回收 vmscan.c
alloc_pages_node NUMA 分配 page_alloc.c
zswap_shrink zswap 压缩池收缩 zswap.c

调试接口

接口 用途
/proc/swaps 交换区信息
/proc/buddyinfo Buddy 空闲块
numastat NUMA 内存统计
cat /sys/kernel/mm/compact_memory 手动内存整理
最后修改: 2026年7月1日

作者

评论

发表评论

您的邮箱地址不会被公开。