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]范围内。这个公式让内存越大的系统保留越多的最低空闲内存,是水位线计算和内存回收触发的基础。