
build_zonerefs_node()是 zonelist 初始化过程中的核心辅助函数负责将一个 NUMA 节点内所有已填充populated的 zone 依次加入zoneref数组。核心作用与背景在 NUMA 系统中每个节点的内存分配顺序由zonelist定义。当构建某个节点的 zonelist 时需要先把本地节点内的各个 zone按 zone 类型从高到低填充进去再去填充远程节点的 zone。build_zonerefs_node()就是完成“填充单个节点”这一步的函数。它遍历一个pg_data_t上的所有 zone 类型把有内存的 zone 通过zoneref_set_zone()写入调用方提供的zonerefs数组并返回实际加入的 zone 数量。核心逻辑该函数的典型实现如下static int build_zonerefs_node(pg_data_t *pgdat, struct zoneref *zonerefs) { struct zone *zone; enum zone_type zone_type MAX_NR_ZONES; int nr_zones 0; do { zone_type--; zone pgdat-node_zones zone_type; if (populated_zone(zone)) { zoneref_set_zone(zone, zonerefs[nr_zones]); check_highest_zone(zone_type); } } while (zone_type); return nr_zones; }关键点解析1. 从高 zone 类型向低遍历循环从MAX_NR_ZONES开始递减即先处理ZONE_MOVABLE、ZONE_HIGHMEM最后处理ZONE_DMA。这与分配时优先使用高端 zone的策略一致。2. 使用populated_zone()判断这是该函数在 2022 年修复的关键点。此前使用的是managed_zone()它要求 zone当前有被 buddy 分配器管理的页。这会导致一个问题当一个 zone 的内存全部被 balloon 走如 Xen guest 中managed_zone()返回假该 zone 会被完全排除出 zonelist后续分配和回收都无法触及它。修复后改用populated_zone()它只要求 zone在物理上存在且有内存段不要求当前有空闲页。这样即使 zone 暂时被 balloon 耗尽它仍保留在 zonelist 中后续 balloon 归还内存时能立即被使用。3.check_highest_zone()的副作用每加入一个 zone就调用check_highest_zone(zone_type)用于更新全局的highest_zoneidx或policy_zone确保系统知道当前可用的最高 zone 类型。4. 返回值加入的 zone 数量函数返回nr_zones调用方据此推进zonerefs数组的起始位置继续填充下一个节点的 zone。在 zonelist 构建中的位置build_zonerefs_node()被build_zonelists()反复调用用于按“从近到远”的顺序填充每个节点的 zonebuild_zonelists(pgdat) ├── build_zonerefs_node(pgdat, zonerefs) // 本地节点 │ └── zonerefs 返回的 nr_zones ├── build_zonerefs_node(NODE_DATA(node1), zonerefs) // 邻近节点 └── ...命名与历史演进该函数原名build_zonelists_node在 2017 年 Michal Hocko 的补丁commit9d3be21bf9c0中被重命名为build_zonerefs_node同时移除了它原本接收和返回“数组索引”的参数改为直接操作zonerefs数组指针只返回加入的 zone 数量。总结build_zonerefs_node()是 zonelist 构建的单节点填充器它从最高 zone 类型向低遍历pgdat-node_zones[]用populated_zone()判断 zone 是否应纳入 zonelist通过zoneref_set_zone()写入数组并调用check_highest_zone()更新全局信息返回加入的 zone 数量供调用方推进数组。2022 年从managed_zone()到populated_zone()的修复确保了内存被 balloon 耗尽的 zone 不会从 zonelist 中消失。