ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

page_alloc calculate_min_free_kbytes

page_alloc calculate_min_free_kbytes

calculate_min_free_kbytes()是内核中用于根据系统内存大小自动计算min_free_kbytes默认值的函数。它通常在启动初始化阶段被init_per_zone_wmark_min()调用。

核心作用与背景

min_free_kbytes是内存管理中最低保留内存的全局参数。它决定了每个 zone 的WMARK_MIN水位,是内核在内存紧张时保留的最后一道防线。用户可以通过/proc/sys/vm/min_free_kbytes手动设置,但如果没有设置,内核需要一个自动计算的默认值。

典型实现

典型的计算逻辑如下:

static void calculate_min_free_kbytes(void) { unsigned long lowmem_kbytes; int new_min_free_kbytes; lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10); new_min_free_kbytes = int_sqrt(lowmem_kbytes * 16); if (new_min_free_kbytes > user_min_free_kbytes) { min_free_kbytes = new_min_free_kbytes; if (min_free_kbytes < 128) min_free_kbytes = 128; if (min_free_kbytes > 262144) min_free_kbytes = 262144; } else { pr_warn("min_free_kbytes is not updated to %d because user defined value " "%d is preferred\n", new_min_free_kbytes, user_min_free_kbytes); } }

逐步解析

1. 计算低端内存大小(KB)

lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10);
  • nr_free_buffer_pages():前面讲过的,统计用户态可用的空闲页总数。

  • PAGE_SIZE >> 10:把页数转换为 KB(假设 PAGE_SIZE 为 4KB,则>> 10即除以 1024)。

  • 结果lowmem_kbytes是直接映射内存的总大小(KB)。

2. 平方根公式计算默认值

new_min_free_kbytes = int_sqrt(lowmem_kbytes * 16);

这是核心公式:min_free_kbytes = sqrt(低端内存大小(KB) × 16)。

  • 1GB 内存:sqrt(1024×1024 × 16) ≈ sqrt(16M) ≈ 4MB;

  • 4GB 内存:约 8MB;

  • 16GB 内存:约 16MB。

  • 搜索结果的注释也印证了这一点:min_free_kbytes = 4 * sqrt(lowmem_kbytes),因为sqrt(lowmem_kbytes * 16) = 4 * sqrt(lowmem_kbytes)。

这个公式的设计意图是:内存越大,保留量按平方根增长——保留比例逐渐减小,但绝对保留量逐渐增加。

3. 与用户设置比较

if (new_min_free_kbytes > user_min_free_kbytes) { min_free_kbytes = new_min_free_kbytes; ... } else { pr_warn("min_free_kbytes is not updated to %d because user defined value " "%d is preferred\n", new_min_free_kbytes, user_min_free_kbytes); }
  • user_min_free_kbytes是用户通过 sysctl 设置的值,初始为-1(表示未设置)。

  • 如果自动计算值大于用户设置值,则采用自动计算值。

  • 否则保留用户设置值,并打印警告。

4. 边界限制

if (min_free_kbytes < 128) min_free_kbytes = 128; if (min_free_kbytes > 262144) min_free_kbytes = 262144;
  • 下限 128KB:防止设置过小导致系统无法正常回收。

  • 上限 262144KB(256MB):历史上曾是 64MB,2020 年的补丁将其提高了 4 倍,以应对现代企业级系统的大内存和高速网卡场景。

历史演进

  • 2005 年:引入min_free_kbytes的平方根计算公式,上限设为 64MB。

  • 2016 年:修复了 khugepaged 初始化顺序问题,确保 THP 推荐值不会被init_per_zone_wmark_min()覆盖。

  • 2020 年:Joel Savitz 的补丁将上限从 64MB 提高到 256MB,以解决高速网卡场景下的分配失败问题。

  • 2023 年:讨论过修改计算规则以包含ZONE_MOVABLE,但未合并。

总结

calculate_min_free_kbytes()是min_free_kbytes默认值的自动计算器:它用sqrt(低端内存(KB) × 16)的平方根公式计算默认值,与用户设置比较后取较大者,并限制在[128KB, 256MB]范围内。这个公式让内存越大的系统保留越多的最低空闲内存,是水位线计算和内存回收触发的基础。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进