定义了几个与迁移类型、内存水位、内存规整(defrag)、NUMA 节点相关的全局变量。逐块拆解。
一、migratetype_names[]—— 迁移类型名称表
const char * const migratetype_names[MIGRATE_TYPES] = { "Unmovable", "Movable", "Reclaimable", "HighAtomic", #ifdef CONFIG_CMA "CMA", #endif #ifdef CONFIG_MEMORY_ISOLATION "Isolate", #endif };作用
和zone_names[]类似,这是迁移类型索引到可读名称的映射表,用于打印、/proc/pagetypeinfo等。
类型
const char * const [...]:指向
const char的常量指针数组;指针本身和字符串内容都不可修改;
比
zone_names[]的char * const更严格(zone_names指向的字符串也是字面量,但类型上没标const char)。
各项含义
| 索引 | 名字 | 含义 |
|---|---|---|
MIGRATE_UNMOVABLE | "Unmovable" | 不可移动页(内核数据结构、页表等),最容易造成碎片 |
MIGRATE_MOVABLE | "Movable" | 可移动页(用户态匿名页、文件页缓存),可规整迁移 |
MIGRATE_RECLAIMABLE | "Reclaimable" | 可回收页(如 slab 缓存),可被回收而非迁移 |
MIGRATE_HIGHATOMIC | "HighAtomic" | 高优先级原子分配保留区,供GFP_ATOMIC等紧急分配 |
MIGRATE_CMA | "CMA" | 连续内存分配器(Contiguous Memory Allocator)区域,条件编译 |
MIGRATE_ISOLATE | "Isolate" | 被隔离的页块,用于内存热插拔/规整,条件编译 |
反碎片的核心思想
内核把空闲页按迁移类型分到不同链表;
分配时尽量同类型分配,避免可移动页混进不可移动页块;
这样可移动页块保持连续、可整块迁移/回收,减少碎片。
当某类型不够时,
__rmqueue_fallback()才去"偷"其他类型的页块(可能破坏反碎片,所以尽量少用)。
与sysctl_lowmem_reserve_ratio的对比
| 数组 | 索引维度 | 用途 |
|---|---|---|
zone_names[] | zone | 打印 zone 名 |
sysctl_lowmem_reserve_ratio[] | zone | 低端保留比例 |
migratetype_names[] | 迁移类型 | 打印迁移类型名 |
三者都是"按枚举索引的配置/名字表"。
二、内存水位与 min_free_kbytes
int min_free_kbytes = 1024; int user_min_free_kbytes = -1; static int watermark_boost_factor __read_mostly = 15000; static int watermark_scale_factor = 10;
这几个变量控制zone 水位(watermark)的计算,即"保留多少空闲内存"。
1.min_free_kbytes = 1024
全局最小值:系统至少要保留1024 KB(1 MB)空闲内存。
是
WMARK_MIN的基础:每个 zone 的
WMARK_MIN按 zone 大小比例分配min_free_kbytes;再基于
WMARK_MIN计算WMARK_LOW、WMARK_HIGH。
用户可通过
/proc/sys/vm/min_free_kbytes调整。
2.user_min_free_kbytes = -1
用户显式设置的值,初始
-1表示"用户未设置"。用于区分:
内核根据内存大小自动计算的
min_free_kbytes;用户通过 sysctl手动设定的值。
若用户设过,重启/热插拔时不应被自动计算覆盖,所以单独存一份用户意图。
3.watermark_boost_factor = 15000(__read_mostly)
水位提升因子,默认 15000(即150%,因为单位是百分比 ×100)。
用于在内存碎片化严重/规整失败时,临时抬高水位,让回收更积极,给规整留出空间。
相关机制:
boost_watermark(),在should_compact_retry()等路径触发。/proc/sys/vm/watermark_boost_factor可调。
4.watermark_scale_factor = 10(默认)
水位间距因子,默认 10(即0.1%)。
决定
WMARK_LOW与WMARK_MIN、WMARK_HIGH与WMARK_LOW之间的间距:间距 ≈ zone_managed_pages * watermark_scale_factor / 10000
间距越大,kswapd 越早被唤醒、回收越积极,但空闲内存保留也越多。
/proc/sys/vm/watermark_scale_factor可调。
水位三档回顾
free pages ^ | WMARK_HIGH ── 高于此,kswapd 停止回收 | WMARK_LOW ── 低于此,kswapd 开始回收 | WMARK_MIN ── 低于此,直接回收/规整,甚至 OOM +-------------------------------->
min_free_kbytes决定WMARK_MIN的基准;watermark_scale_factor决定三档之间的间距;watermark_boost_factor在需要时临时抬高水位。
三、defrag_mode
int defrag_mode;
控制内存规整(compaction)行为模式的开关。
相关 sysctl:
/proc/sys/vm/compaction_proactiveness等(不同版本语义有差异)。用于决定是否启用主动规整、规整的积极程度等。
默认 0,表示默认行为。
(不同内核版本defrag_mode的具体含义略有不同,但都属于"内存规整策略"的全局配置。)
四、movable_zone—— ZONE_MOVABLE 的来源 zone
/* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */ int movable_zone; EXPORT_SYMBOL(movable_zone);
含义
ZONE_MOVABLE是一个虚拟 zone:它的页不是独立物理内存,而是从某个"真实 zone"里划出来的。movable_zone记录这个真实 zone 的索引(通常是ZONE_NORMAL或ZONE_HIGHMEM)。注释说得很清楚:
"real" zone pages in ZONE_MOVABLE are taken from。
为什么需要它?
ZONE_MOVABLE用于:内存热插拔:可移动页能整块迁移走,方便下线内存;
减少碎片:可移动页集中管理,便于规整;
hugetlb 大页:需要连续内存,放 MOVABLE 更易满足。
但它的物理页来自真实 zone,所以分配/统计时需要知道来源 zone。
例如迁移类型 fallback、水位计算、页归属判断都要用到
movable_zone。
设置时机
内核启动参数
kernelcore=/movablecore=指定 MOVABLE 大小;初始化时据此从某个真实 zone 划出 MOVABLE,并记录
movable_zone。
EXPORT_SYMBOL
导出给模块(如 CMA、hugetlb 相关模块可能需要知道来源 zone)。
五、NUMA 节点数量变量(MAX_NUMNODES > 1)
#if MAX_NUMNODES > 1 unsigned int nr_node_ids __read_mostly = MAX_NUMNODES; unsigned int nr_online_nodes __read_mostly = 1; EXPORT_SYMBOL(nr_node_ids); EXPORT_SYMBOL(nr_online_nodes); #endif
条件编译MAX_NUMNODES > 1
只在可能多节点(NUMA 或
CONFIG_NODES_SHIFT > 0)时才定义。单节点(
MAX_NUMNODES == 1)时不需要,编译期就确定只有一个节点。
1.nr_node_ids
unsigned int nr_node_ids __read_mostly = MAX_NUMNODES;
实际存在的节点 ID 数量上限(稀疏节点编号下的最大 ID + 1)。
初始为
MAX_NUMNODES(最宽松),启动时根据固件探测收紧。用途:
分配
node_data[]、node_states等数组时确定大小;遍历节点时作为边界;
允许节点 ID 稀疏(比如只有 node 0 和 node 3),此时
nr_node_ids = 4,但nr_online_nodes = 2。
2.nr_online_nodes
unsigned int nr_online_nodes __read_mostly = 1;
在线节点数量(实际有内存/CPU 且已上线的节点个数)。
初始 1(至少 node 0),启动时随节点上线增加。
用途:
判断是否真的多节点(
nr_online_nodes > 1);决定是否启用 NUMA 相关优化(如 per-node 分配、NUMA balancing);
统计信息展示。
两者区别
| 变量 | 含义 | 例子(node 0,3 在线) |
|---|---|---|
nr_node_ids | 最大节点 ID + 1(数组维度) | 4 |
nr_online_nodes | 实际在线节点数 | 2 |
__read_mostly+EXPORT_SYMBOL
启动时确定、运行期几乎只读,放 read-mostly 段。
导出给模块使用。
六、小结表
| 变量 | 类型/默认 | 作用 |
|---|---|---|
migratetype_names[] | const char * const [] | 迁移类型名称表 |
min_free_kbytes | int/ 1024 | 系统保留的最小空闲内存(KB) |
user_min_free_kbytes | int/ -1 | 用户显式设置值,-1 表示未设 |
watermark_boost_factor | int/ 15000 | 临时抬高水位的因子 |
watermark_scale_factor | int/ 10 | 水位间距因子 |
defrag_mode | int/ 0 | 内存规整模式 |
movable_zone | int/ 未初始化 | ZONE_MOVABLE 页的真实来源 zone |
nr_node_ids | unsigned int/ MAX_NUMNODES | 最大节点 ID + 1 |
nr_online_nodes | unsigned int/ 1 | 在线节点数 |
一句话总结
这段定义了迁移类型名称表
migratetype_names[],以及一组控制内存水位与规整的全局参数(min_free_kbytes、user_min_free_kbytes、watermark_boost_factor、watermark_scale_factor、defrag_mode)、记录ZONE_MOVABLE来源真实 zone 的movable_zone,以及多节点下的nr_node_ids(最大节点 ID+1)和nr_online_nodes(在线节点数)——它们共同构成 zone/迁移类型/水位/NUMA 的基础配置,多数在启动时确定、运行期只读。