setup_per_zone_lowmem_reserve()是内存管理中用于初始化每个 zone 的lowmem_reserve[]数组的函数。它根据 sysctl 配置的比例和 zone 的 managed_pages计算出每个 zone 需要为更高 zone 的分配保留多少页。核心作用与背景sysctl_lowmem_reserve_ratio[]低端 zoneDMA、DMA32、Normal需要保留一部分内存防止高端 zone 的分配回退时把它们耗尽。保留量 上方 zone 总大小 / ratio。setup_per_zone_lowmem_reserve()就是把这个比例实际计算成每个 zone 的lowmem_reserve[]数组。它在 zone 初始化、内存热插拔、sysctl 修改时被调用。典型实现static void setup_per_zone_lowmem_reserve(void) { struct pglist_data *pgdat; enum zone_type i, j; for_each_online_pgdat(pgdat) { for (i 0; i MAX_NR_ZONES - 1; i) { struct zone *zone pgdat-node_zones[i]; int ratio sysctl_lowmem_reserve_ratio[i]; bool clear !ratio || !zone-managed_pages; unsigned long managed_pages 0; /* * 累加所有比 i 更高的 populated zone 的 managed_pages */ for (j i 1; j MAX_NR_ZONES; j) { struct zone *upper_zone pgdat-node_zones[j]; if (populated_zone(upper_zone)) managed_pages upper_zone-managed_pages; } if (clear) zone-lowmem_reserve[i] 0; else zone-lowmem_reserve[i] managed_pages / ratio; } } }逐步解析1. 遍历所有在线节点for_each_online_pgdat(pgdat)每个 NUMA 节点独立计算自己的 lowmem_reserve。2. 遍历 zone除最高 zone 外for (i 0; i MAX_NR_ZONES - 1; i)lowmem_reserve[]只对不是最高 zone的 zone 有意义——最高 zone 之上没有更高 zone 会回退进来不需要保留。所以循环到MAX_NR_ZONES - 1。3. 获取比例int ratio sysctl_lowmem_reserve_ratio[i];从 sysctl 数组读取该 zone 的保留比例前面讲过的 256/32/0 等。4. 累加上方 zone 的 managed_pagesfor (j i 1; j MAX_NR_ZONES; j) { struct zone *upper_zone pgdat-node_zones[j]; if (populated_zone(upper_zone)) managed_pages upper_zone-managed_pages; }遍历所有比 i 更高的 zone累加它们的managed_pages。只有 populated zone 才计入。这对应前面注释中的公式保留量 上方 zone 总大小 / ratio。5. 计算保留量if (clear) zone-lowmem_reserve[i] 0; else zone-lowmem_reserve[i] managed_pages / ratio;clear条件ratio 0如 HIGHMEM、MOVABLE不保留或zone-managed_pages 0zone 没有管理页。此时保留量为 0。否则保留量 上方 managed_pages / ratio。示例计算以 1GB 机器DMA 16MB、Normal 784MB、HIGHMEM 224MB为例izoneratio上方 managed_pageslowmem_reserve[i]ZONE_DMADMA256784M 224M 1008M1008M/256 ≈ 3.9MZONE_NORMALNormal32224M224M/32 7MZONE_HIGHMEMHighMem0—0clear结果与前面注释中的描述一致。调用场景它被以下函数调用free_area_init_core()/free_area_init()zone 初始化时setup_per_zone_wmarks()水位调整后内存热插拔zone 上线/下线后sysctl 修改调整vm.lowmem_reserve_ratio后。与calculate_totalreserve_pages()的关系setup_per_zone_lowmem_reserve()计算每个 zone 的保留量写入zone-lowmem_reserve[]calculate_totalreserve_pages()前面讲过的汇总所有保留量到totalreserve_pages。通常调用顺序是先setup_per_zone_lowmem_reserve()再setup_per_zone_wmarks()最后calculate_totalreserve_pages()。总结setup_per_zone_lowmem_reserve()是低端内存保留的计算器它遍历所有在线节点和 zone对每个非最高 zone累加所有更高 populated zone 的managed_pages除以sysctl_lowmem_reserve_ratio[i]得到该 zone 的保留量。比例为 0 或 zone 无管理页时保留量为 0。它确保低端 zoneDMA、DMA32、Normal在高端分配回退时有足够的保留内存是反碎片和内存保护机制的核心实现。