内存管理-77-kernel-internals系列-Memory专题-Advanced-4-Memory Compaction
一、Memory Compaction 翻译
注: 翻译自 https://kernel-internals.org/mm/compaction/
内存规整/内存压缩 (Memory Compaction)
为大块内存分配而对物理内存进行碎片整理。
1. 什么是规整?
内存规整通过移动页面,来创建出连续的空闲区域。随着时间推移,空闲内存会变得碎片化——以小碎片的形式散布在整个物理内存中。规整会把这些碎片合并起来。
规整前: ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │ U │ F │ U │ F │ U │ U │ F │ U │ F │ F │ U │ F │ U │ F │ U │ F │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ U = 已使用,F = 空闲(零散分布) 规整后: ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │ U │ U │ U │ U │ U │ U │ U │ U │ U │ F │ F │ F │ F │ F │ F │ F │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ 创建出连续的空闲区域
2. 为什么需要规整?
2.1 碎片化问题
伙伴分配器 (buddy allocator) 需要物理上连续的页面,来满足高阶(high-order)分配:
------------------------------------------------------------------------------------------------------------------------------------- 阶(Order) 大小 用途 ------------------------------------------------------------------------------------------------------------------------------------- 0 4KB 普通页面 1 8KB 一些内核分配 2 16KB 巨型帧 Jumbo frames 3+ 32KB+ 大页 Huge pages、DMA 缓冲区 -------------------------------------------------------------------------------------------------------------------------------------
系统运行几天/几周之后,可能空闲内存还很多,但已经没有连续区域可用于高阶分配了。
2.2 在规整出现之前
在有规整之前,高阶分配失败很常见:
THP 无法分配 2MB 页面
DMA 缓冲区分配失败
网络巨型帧不可用
唯一的解决办法是重启,或者在启动时预留内存。
3. 规整是如何工作的?
3.1 算法
规整使用两个相向移动的扫描器:
Zone start Zone end
│ │
▼ ▼
┌───────────────────────────────────────────────────────────────┐
│ Zone │
└───────────────────────────────────────────────────────────────┘
│ ▼
▼ 空闲扫描器
迁移扫描器 (寻找空闲页面)
(寻找可移动页面) │
│ │
└──────────────► ◄─────────────────────────────────────────────┘
在中间相遇
(1) 空闲扫描器 (Free scanner):从 zone 末尾开始,向后移动,寻找空闲页;
(2) 迁移扫描器 (Migration scanner):从 zone 起始开始,向前移动,寻找可移动页;
(3) 迁移 (Migration):把迁移扫描器找到的页面,搬到空闲扫描器找到的位置;
(4) 结果:空闲空间被合并到一端;
3.2 页面可移动性
不是所有页面都能移动:
------------------------------------------------------------------------------------------------------------------------------------- 迁移类型 是否可移动? 示例 ------------------------------------------------------------------------------------------------------------------------------------- MIGRATE_MOVABLE 是 用户页面、页面缓存 page cache MIGRATE_RECLAIMABLE 有时可以 缓存(可以直接释放,而不移动) MIGRATE_UNMOVABLE 否 内核分配、DMA -------------------------------------------------------------------------------------------------------------------------------------
规整只对可移动页面有效。不可移动页面会造成永久性碎片。
4. 规整的触发方式
4.1 直接规整 Direct Compaction(同步)
当高阶分配失败时,执行分配的进程自己运行规整:
alloc_pages(order=9) /* 为 THP 分配 2MB */ │ ▼ 分配失败 │ ▼ 直接规整(进程阻塞等待) │ ▼ 重试分配
4.2 kcompactd(异步)
后台内核线程,主动进行规整:
# 每个 NUMA 节点一个 kcompactd ps aux | grep kcompactd # kcompactd0, kcompactd1, ...
在以下情况被唤醒:
(1) 水位线 (watermark) 显示存在碎片;
(2) 高阶分配正在失败;
(3) 被显式触发;
4.3 手动触发
# 在所有节点上触发规整,实测看起来是在echo线程上下文执行的 echo 1 > /proc/sys/vm/compact_memory # 按节点触发,非NUMA没有此节点 echo 1 > /sys/devices/system/node/node0/compact
5. 配置
5.1 主动规整 Proactive Compaction (v5.9+, 5.4内核上没有)
在分配真正需要之前,就在后台提前规整:
# 开启主动规整(0-100,0=关闭) cat /proc/sys/vm/compaction_proactiveness echo 20 > /proc/sys/vm/compaction_proactiveness # 数值越高 = 后台规整越激进 # 数值越低 = CPU 开销越小,但直接规整越多,实测默认是20
5.2 规整行为
# 碎片指数阈值 # 当 fragmentation_index > extfrag_threshold 时触发规整 # 指数 0 = 因缺内存而失败,1000 = 因碎片而失败 cat /proc/sys/vm/extfrag_threshold # 500(默认值)- 越低越激进(在碎片程度较低时就规整) # 查看每个 order 的碎片指数 cat /sys/kernel/debug/extfrag/extfrag_index
6. 监控
6.1 规整统计
cat /proc/vmstat | grep compact # compact_migrate_scanned - 为迁移而扫描的页面数 # compact_free_scanned - 为找空闲空间而扫描的页面数 # compact_isolated - 为迁移而隔离出来的页面数 # compact_stall - 直接规整导致的停顿次数 # compact_fail - 规整失败次数 # compact_success - 规整成功次数
6.2 碎片指数
# 每个 order 的碎片情况(0.0 = 无碎片,1.0 = 严重碎片) cat /sys/kernel/debug/extfrag/extfrag_index # 示例: # Node 0, zone Normal # order 0 1 2 3 4 5 6 7 8 9 10 # index 0.0 0.0 0.0 0.0 0.1 0.2 0.4 0.6 0.8 0.9 1.0
6.3 追踪 Tracing
# 追踪规整事件 echo 1 > /sys/kernel/debug/tracing/events/compaction/mm_compaction_begin/enable echo 1 > /sys/kernel/debug/tracing/events/compaction/mm_compaction_end/enable cat /sys/kernel/debug/tracing/trace_pipe
7. 演进历史
引入 (v2.6.35, 2010)
提交:748446bb6b5a ("mm: compaction: memory compaction core")
作者:Mel Gorman
最初的规整实现,用于支持 THP,减少高阶分配失败。
kcompactd (v4.6, 2016)
提交:698b1b30642f ("mm, compaction: introduce kcompactd")
作者:Vlastimil Babka
后台规整守护线程,类似于负责回收的 kswapd。
主动规整 (v5.9, 2020)
提交:facdaa917c4d ("mm: proactive compaction")
作者:Nitin Gupta
基于碎片程度主动规整,减少直接规整导致的停顿。
8. 规整 vs 回收
两者都在内存压力下运行,但目的不同:
------------------------------------------------------------------------------------------------------------------------------------- 方面 回收 Reclaim 规整 Compaction ------------------------------------------------------------------------------------------------------------------------------------- 目标 释放出空闲内存 创建连续区域 时机 空闲页面不足时 高阶分配失败时 动作 驱逐/换出页面 移动页面 守护线程 kswapd kcompactd -------------------------------------------------------------------------------------------------------------------------------------
它们经常协同工作:回收负责释放页面,规整负责把页面排列整齐。
9. 常见问题
9.1 直接规整停顿
进程阻塞等待规整,导致卡住。
症状:延迟毛刺、compact_stall 计数很高
解决办法:开启主动规整; 调优 compaction_proactiveness; 减少高阶分配
9.2 规整失败
无法创建出连续区域。
症状:compact_fail 很高、THP 分配失败
原因:不可移动页面太多; 碎片太严重
解决办法:减少内核内存使用; 增大可移动 zone 的大小; 考虑用内存热插拔来做隔离; kcompactd CPU 占用过高;
9.3 后台规整消耗过多 CPU。
排查:top、perf top
解决办法:降低 compaction_proactiveness; 接受更多的直接规整.
10. 参考资料
9.1 关键代码
------------------------------------------------------------------------------------------------------------------------------------- 文件 说明 ------------------------------------------------------------------------------------------------------------------------------------- mm/compaction.c 规整的实现 mm/page_alloc.c 规整的触发点 -------------------------------------------------------------------------------------------------------------------------------------
page-allocator - 伙伴系统、迁移类型
thp - 规整的主要消费者
reclaim - 与规整协同工作
contiguous-memory - 为什么连续分配会失败及解决方案
11. 进一步阅读
page-allocator.md — 伙伴分配器的迁移类型(MIGRATE_MOVABLE、MIGRATE_UNMOVABLE),决定了 compaction 可以移动哪些页面
thp.md — 透明大页:compaction 的主要驱动因素;需要 order-9 的连续物理页
reclaim.md — kswapd 和直接回收;compaction 与回收在相同的水位线逻辑下协同工作
cma.md — 连续内存分配器;使用基于迁移的 compaction 来回收预留的 CMA 区域
mm/compaction.c — 完整的 compaction 实现;compact_zone() 运行双扫描算法
mm/page_alloc.c — try_to_compact_pages() 是在分配失败时触发直接 compaction 的位置
LWN: Memory compaction — Mel Gorman 对双扫描算法和碎片化指数的介绍
LWN: Proactive compaction — Nitin Gupta 在 v5.9 中加入的主动式 compaction,以及 compaction_proactiveness 可调参数
748446bb6b5a — Mel Gorman 在 v2.6.35 中提交的 compaction 核心原始提交
facdaa917c4d — 在 v5.9 中加入的主动式 compaction,用于减少直接 compaction 造成的停顿
Documentation/admin-guide/mm/transhuge.rst — 涵盖 THP 分配、compaction 调优和 compact_memory sysctl
posted on 2026-10-06 15:07 Hello-World3 阅读(4) 评论(0) 收藏 举报
浙公网安备 33010602011771号