min_free_kbytes_sysctl_handler()是/proc/sys/vm/min_free_kbytes的 sysctl 处理函数,负责响应用户对该参数的读写请求。
核心实现
static int min_free_kbytes_sysctl_handler(const struct ctl_table *table, int write, void *buffer, size_t *length, loff_t *ppos) { int rc; rc = proc_dointvec_minmax(table, write, buffer, length, ppos); if (rc) return rc; if (write) { user_min_free_kbytes = min_free_kbytes; setup_per_zone_wmarks(); } return 0; }逐步解析
1. 委托标准整数读写
rc = proc_dointvec_minmax(table, write, buffer, length, ppos); if (rc) return rc;
proc_dointvec_minmax():标准 sysctl 整数处理函数,负责把用户输入的字符串解析为整数并写入table->data(即min_free_kbytes),或把当前值读出给用户。带
_minmax后缀表示它在写入时会检查值是否在table->extra1(最小值)和table->extra2(最大值)范围内。如果解析或范围检查失败,直接返回错误。
2. 写操作后的额外处理
if (write) { user_min_free_kbytes = min_free_kbytes; setup_per_zone_wmarks(); }只有写操作才需要:
user_min_free_kbytes = min_free_kbytes:记录用户显式设置的值。user_min_free_kbytes初始为-1,用于区分"用户未设置"和"用户设置了某个值"。这里把它更新为当前值,以便后续calculate_min_free_kbytes()知道用户已经有明确设置,不应被自动计算覆盖。setup_per_zone_wmarks():min_free_kbytes变了,必须重新计算所有 zone 的WMARK_MIN/WMARK_LOW/WMARK_HIGH。这个调用会获取pcp_batch_high_lock并调用__setup_per_zone_wmarks()。
3. 读操作
如果write == 0,函数只执行proc_dointvec_minmax()把当前min_free_kbytes值复制到用户缓冲区,不做额外处理。
sysctl 表定义
这个 handler 通常注册在vm_table[]中:
{ .procname = "min_free_kbytes", .data = &min_free_kbytes, .maxlen = sizeof(min_free_kbytes), .mode = 0644, .proc_handler = min_free_kbytes_sysctl_handler, .extra1 = SYSCTL_ZERO, },.data指向全局min_free_kbytes;.proc_handler是本函数;.extra1设最小值 0(或某个下限)。
与相关函数的关系
| 函数 | 职责 |
|---|---|
min_free_kbytes_sysctl_handler() | sysctl 入口:读写min_free_kbytes,写时更新水位 |
proc_dointvec_minmax() | 标准整数读写 |
setup_per_zone_wmarks() | 重新计算各 zone 水位 |
user_min_free_kbytes | 记录用户显式设置值 |
calculate_min_free_kbytes() | 启动时自动计算默认值 |
历史演进
早期版本的 handler 在写操作后还会调用setup_per_zone_lowmem_reserve(),因为lowmem_reserve的计算也依赖managed_pages,而min_free_kbytes的变化可能间接影响它。但在现代内核中,lowmem_reserve的计算已独立,只在 zone 大小变化时更新,因此 handler 只调用setup_per_zone_wmarks()。
总结
min_free_kbytes_sysctl_handler()是vm.min_free_kbytes的sysctl 读写入口:它先通过proc_dointvec_minmax()完成标准的整数读写和范围检查,如果是写操作,则记录用户设置值到user_min_free_kbytes并调用setup_per_zone_wmarks()重新计算所有 zone 的水位线。它确保用户对最低保留内存的调整能立即生效,并防止被后续的自动计算覆盖。