zone_batchsize()是 PCP(Per-CPU Pages)机制中的一个关键计算函数,用于根据 zone 的大小动态确定该 zone 的 PCP 批量大小(pcp->batch)。批量大小决定了每次从伙伴系统补充或归还 PCP 缓存时,一次操作多少页。
核心作用与设计动机
PCP 的batch值需要在减少锁竞争和控制内存滞留之间取得平衡:
batch 太小:频繁从伙伴系统补充/归还,锁竞争激烈,性能差;
batch 太大:单个 CPU 可能囤积过多页,导致其他 CPU 或内存回收无法使用这些页,内存利用率下降。
zone_batchsize()根据 zone 的管理页数(managed_pages)按比例计算一个合理的批量。
典型实现
static int zone_batchsize(struct zone *zone) { int batch; /* * 当 batch 为 1 时,PCP 缓存几乎无意义(每次只补一页), * 这里给出一个合理的下限。 * * 每个 CPU 大致可以接受最多 1/4 MB 的内存被“困”在 PCP 中。 * 假设有 NR_CPUS 个 CPU,zone 大小 / NR_CPUS / 4 就是每个 * CPU 能分到的份额。 * * 实际上不能这么做,因为 NR_CPUS 可能很大但实际 CPU 很少。 * 这里用 zone 大小的比例来计算。 */ batch = zone->managed_pages / 1024; if (batch < 1) batch = 1; /* * 将 batch 向上取整到 2 的幂,并限制在 [1, 512] 之间。 * 512 页(2MB)是经验上的合理上限。 */ batch = roundup_pow_of_two(batch + batch / 2); if (batch > 512) batch = 512; return batch; }(实际实现随内核版本有差异,以下是核心逻辑的梳理。)
逐段解析
1. 按 zone 大小计算初始 batch
batch = zone->managed_pages / 1024; if (batch < 1) batch = 1;
managed_pages是 zone 被伙伴系统管理的页数(排除 reserved 等)。除以 1024 意味着:zone 越大,batch 越大,但增长是线性的。
下限为 1,保证小 zone 也能正常工作。
2. 向上取整到 2 的幂,并放大 1.5 倍
batch = roundup_pow_of_two(batch + batch / 2);
先放大 1.5 倍(
batch + batch/2),再向上取整到 2 的幂。取 2 的幂是为了对齐方便(伙伴系统、PCP 链表操作都偏好 2 的幂)。
放大 1.5 倍是为了给 batch 留些余量,减少补充频率。
3. 上限 512 页
if (batch > 512) batch = 512;
512 页 = 2MB,是经验上的合理上限。
防止大内存机器上单个 CPU 囤积过多页,影响内存利用率和回收效率。
历史演进:从batch到high_min/high_max
2023 年前后,PCP 的调节机制经历了重要重构:
旧机制:zone_batchsize()计算batch,然后zone_pcp_init()用batch推导high(高水位),关系大致是high = batch * 6。batch和high都是静态的,启动后不再变化。
新机制:引入high_min/high_max,pcp->high可以在运行时动态增长和衰减:
nr_pcp_alloc()在分配压力大时抬高high;decay_pcp_high()在压力小时降低high;batch本身仍由zone_batchsize()计算,但high的调节更灵活。
这个重构让 PCP 能自适应工作负载,在大内存机器上更高效,在内存紧张时更快回流。
调用位置
zone_batchsize()被zone_pcp_init()调用:
static void __meminit zone_pcp_init(struct zone *zone) { zone->per_cpu_pageset = &boot_pageset; if (populated_zone(zone)) zone->pageset = &boot_pageset; if (zone->present_pages) pr_debug(" %s zone: %lu pages, LIFO batch:%u\n", zone->name, zone->present_pages, zone_batchsize(zone)); }zone_batchsize()的计算结果会影响后续per_cpu_pages_init()中pcp->batch的初始化。
总结
zone_batchsize()是PCP 批量大小的计算器:它根据 zone 的managed_pages按比例(1/1024)计算初始 batch,放大 1.5 倍并向上取整到 2 的幂,最后限制在 [1, 512] 页之间。它平衡了锁竞争(batch 大)和内存滞留(batch 小)的矛盾,是 PCP 机制自适应不同 zone 大小的关键。2023 年后的重构让high水位能动态调节,而batch仍是这个函数计算的基础值。