zone_names[]——每个 zone 类型对应的人类可读名称字符串数组。它是 zone 的"名字表",用于打印、调试、/proc输出等。mcrjnjfbr
一、作用与类型
char * const zone_names[MAX_NR_ZONES] = { ... };类型:
char * const [MAX_NR_ZONES]char *:指向字符串的指针。const修饰指针本身:数组元素(指针)不可修改,即初始化后不能再让zone_names[i]指向别的字符串。指向的字符串内容是字面量(本身就在只读段),也不能改。
维度:
MAX_NR_ZONES,即系统支持的最大 zone 数量。索引:用
ZONE_DMA、ZONE_DMA32、ZONE_NORMAL……这些枚举值作为下标。值:对应的名字字符串,如
"DMA"、"Normal"。
核心用途:根据 zone 索引快速拿到可打印的名字。
printk("zone %s: ...", zone_names[zone_idx]);比如/proc/zoneinfo、/proc/buddyinfo、内存热插拔日志、OOM 报告等都会用到。
二、逐项解析
1.ZONE_DMA→"DMA"
#ifdef CONFIG_ZONE_DMA "DMA", #endif
只在配置了
CONFIG_ZONE_DMA的架构上存在。对应16MB 以下的 DMA 可用内存(老式 ISA 设备)。
2.ZONE_DMA32→"DMA32"
#ifdef CONFIG_ZONE_DMA32 "DMA32", #endif
只在配置了
CONFIG_ZONE_DMA32的架构上存在(典型是x86_64)。对应4GB 以下、32 位设备可寻址的内存。
3.ZONE_NORMAL→"Normal"
"Normal",
无条件存在,所有架构都有。
内核可直接访问的普通内存,是分配的主力。
4.ZONE_HIGHMEM→"HighMem"
#ifdef CONFIG_HIGHMEM "HighMem", #endif
只在 32 位架构(如 x86_32、ARM 某些配置)上存在。
高端内存,内核不能直接线性映射,需
kmap临时映射。
5.ZONE_MOVABLE→"Movable"
"Movable",
无条件存在(现代内核)。
可移动内存 zone,用于减少碎片、支持内存热插拔。
用户态可移动页优先放这里,便于规整/迁移。
6.ZONE_DEVICE→"Device"
#ifdef CONFIG_ZONE_DEVICE "Device", #endif
只在配置了
CONFIG_ZONE_DEVICE时存在。对应设备内存(如 GPU 显存、持久内存 PMEM、HMM 设备内存),通过
devm_memremap_pages()注册到内存管理。特殊:这些页可能不对应真正的 RAM。
三、为什么用指定初始化器?
注意这里没有写[ZONE_DMA] = "DMA",而是按顺序列出:
char * const zone_names[MAX_NR_ZONES] = { #ifdef CONFIG_ZONE_DMA "DMA", // 对应 ZONE_DMA #endif #ifdef CONFIG_ZONE_DMA32 "DMA32", // 对应 ZONE_DMA32 #endif "Normal", // 对应 ZONE_NORMAL ... };这是顺序初始化,依赖于zone 枚举的定义顺序与数组顺序严格一致。
zone 枚举在
include/linux/mmzone.h中定义,顺序正是:ZONE_DMA, ZONE_DMA32, ZONE_NORMAL, ZONE_HIGHMEM, ZONE_MOVABLE, ZONE_DEVICE, ...
条件编译保证:开启哪个 zone,数组里就补上对应项,顺序仍对齐。
这是一种隐式约定:如果枚举顺序改了,这里也必须同步改,否则名字会错位。
对比前面的sysctl_lowmem_reserve_ratio[]用的是[ZONE_DMA] = 256指定初始化器,那种写法更安全(不依赖顺序)。而这里用顺序初始化,可能是历史原因或为了简洁,但维护时需要小心。
四、与MAX_NR_ZONES
MAX_NR_ZONES是 zone 类型的最大数量(枚举总数),编译期常量。数组大小必须 ≥ 实际 zone 数量。
条件编译保证:只有实际存在的 zone 才占数组项,未定义项默认为
NULL(因为数组是静态存储,未初始化元素为 0)。所以
zone_names[idx]对未编译进来的 zone 是NULL,访问前应确保 idx 有效。
五、典型使用场景
1./proc/zoneinfo、/proc/buddyinfo
Node 0, zone DMA Node 0, zone DMA32 Node 0, zone Normal
这些名字就来自zone_names[]。
2. 打印 zone 信息
pr_info("Node %d, zone %s\n", nid, zone_names[zone_idx]);3. 内存热插拔 / OOM 报告
热插拔日志会打印 zone 名。
OOM killer 报告里也会显示 zone。
4. 调试与追踪
dump_page()、show_free_areas()等会用到。
六、与前面内容的呼应
| 前面出现过的 | 这里的对应 |
|---|---|
sysctl_lowmem_reserve_ratio[MAX_NR_ZONES] | 同样以 zone 枚举为索引 |
ZONE_DMA/ZONE_DMA32/ZONE_NORMAL/ZONE_HIGHMEM/ZONE_MOVABLE | 这里的数组项一一对应 |
CONFIG_ZONE_DEVICE | 新增的"Device"项 |
MAX_NR_ZONES | 数组维度 |
它们共同构成"zone 维度的配置/名字表",是 zone 相关代码的基础设施。
七、小结表
| 索引(枚举) | 名字 | 条件 | 含义 |
|---|---|---|---|
ZONE_DMA | "DMA" | CONFIG_ZONE_DMA | 16MB 以下 DMA 内存 |
ZONE_DMA32 | "DMA32" | CONFIG_ZONE_DMA32 | 4GB 以下 32 位可寻址 |
ZONE_NORMAL | "Normal" | 无条件 | 内核可直接访问的普通内存 |
ZONE_HIGHMEM | "HighMem" | CONFIG_HIGHMEM | 高端内存(需 kmap) |
ZONE_MOVABLE | "Movable" | 无条件 | 可移动内存,反碎片/热插拔 |
ZONE_DEVICE | "Device" | CONFIG_ZONE_DEVICE | 设备内存(GPU/PMEM/HMM) |
一句话总结
zone_names[]是 zone 索引到可读名称的映射表,按 zone 枚举顺序用条件编译逐项填充,供打印、/proc输出和调试使用;它依赖枚举与数组顺序严格一致,未编译进的 zone 项为NULL。