[TOC]
mm/util.c
init_user_buckets: 初始化memdup_user的专用内存桶
此代码片段的作用是在内核启动的早期阶段, 创建一个专用的、高性能的内存分配器(一个kmem_buckets
实例), 并将其赋给全局指针user_buckets
。这个专用的分配器被命名为"memdup_user"
, 意味着它被内核中的memdup_user()
函数和相关函数独占使用, 目的是优化从用户空间复制数据到内核空间时的小块内存分配性能。
工作原理:
memdup_user()
是一个非常常见的内核函数, 用于安全地从用户空间拷贝一块数据到内核新分配的内存中。这个操作在处理系统调用参数、网络数据包等场景中被频繁调用, 且请求分配的内存大小各不相同。
标准的通用内存分配器kmalloc()
虽然功能强大, 但对于这种极其频繁、大小多变的小块内存请求, 其内部的查找和管理开销可能会成为性能瓶颈。
kmem_buckets
(内存桶) 则是一种更轻量级、更快的专用分配器, 类似一个内存池(memory pool)。
- 它内部预先维护了一系列”桶”(bucket), 每个桶中存放着特定大小(通常是2的幂次方)的内存块。
- 当请求一个特定大小的内存时, 它会非常迅速地找到能容纳该大小的最小的那个桶, 并从桶中取出一个现成的内存块返回。
- 这避免了
kmalloc()
中相对复杂的slab/slub缓存查找过程, 减少了分配延迟, 并可能因为重复使用相同大小的内存块而提高CPU缓存的命中率。
init_user_buckets
函数就是用来创建这样一个专为memdup_user()
优化的内存池。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
static kmem_buckets *user_buckets __ro_after_init;
static int __init init_user_buckets(void) {
user_buckets = kmem_buckets_create("memdup_user", 0, 0, INT_MAX, NULL);
return 0; }
subsys_initcall(init_user_buckets);
|
Linux内核内存过量使用(Overcommit)的Sysctl控制
此代码片段的作用是在Linux内核中注册一组sysctl
接口, 以便系统管理员可以通过/proc/sys/vm/
目录下的文件, 动态地调整内核的内存过量使用(memory overcommit)策略。
内存过量使用是现代虚拟内存管理系统的一个核心特性。当一个应用程序请求内存(例如通过malloc()
)时, 内核并不会立即分配真实的物理RAM, 而是仅仅在进程的虚拟地址空间中做出一个”承诺”。只有当应用程序第一次写入该地址时, 内核才会通过缺页中断(page fault)分配一个物理页帧。过量使用策略允许系统”承诺”出去的虚拟内存总量可以远超实际可用的物理RAM和交换空间(swap)之和。这通常能提高内存利用率, 因为很多程序申请了大量内存但并不会完全使用。然而, 这也带来了风险: 如果所有程序都开始使用它们被承诺的内存, 系统可能会耗尽所有可用内存, 从而触发OOM (Out-Of-Memory) Killer来强制杀死进程。
这组sysctl
接口就是用来让管理员根据应用负载的特点, 精确地控制这个”承诺”的风险等级的。
特殊的proc_handler
函数
这些函数是当用户读/写对应的/proc/sys/vm/*
文件时, 内核调用的处理程序。它们在标准的读写整数值之外增加了额外的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
static int overcommit_ratio_handler(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { int ret;
ret = proc_dointvec(table, write, buffer, lenp, ppos); if (ret == 0 && write) sysctl_overcommit_kbytes = 0; return ret; }
static void sync_overcommit_as(struct work_struct *dummy) { percpu_counter_sync(&vm_committed_as); }
static int overcommit_policy_handler(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table t; int new_policy = -1; int ret;
if (write) { t = *table; t.data = &new_policy; ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos); if (ret || new_policy == -1) return ret;
mm_compute_batch(new_policy);
if (new_policy == OVERCOMMIT_NEVER) schedule_on_each_cpu(sync_overcommit_as); sysctl_overcommit_memory = new_policy; } else { ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); }
return ret; }
static int overcommit_kbytes_handler(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { int ret;
ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write) sysctl_overcommit_ratio = 0; return ret; }
|
sysctl
表定义与注册
这部分代码定义了将在/proc/sys/vm/
下创建哪些文件, 以及它们如何与内核变量和处理函数关联。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #define OVERCOMMIT_GUESS 0 #define OVERCOMMIT_ALWAYS 1 #define OVERCOMMIT_NEVER 2
int sysctl_overcommit_memory __read_mostly = OVERCOMMIT_GUESS; static int sysctl_overcommit_ratio __read_mostly = 50; static unsigned long sysctl_overcommit_kbytes __read_mostly; int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT; unsigned long sysctl_user_reserve_kbytes __read_mostly = 1UL << 17; unsigned long sysctl_admin_reserve_kbytes __read_mostly = 1UL << 13;
static const struct ctl_table util_sysctl_table[] = { { .procname = "overcommit_memory", .data = &sysctl_overcommit_memory, .maxlen = sizeof(sysctl_overcommit_memory), .mode = 0644, .proc_handler = overcommit_policy_handler, .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_TWO, }, { .procname = "overcommit_ratio", .data = &sysctl_overcommit_ratio, .maxlen = sizeof(sysctl_overcommit_ratio), .mode = 0644, .proc_handler = overcommit_ratio_handler, }, { .procname = "overcommit_kbytes", .data = &sysctl_overcommit_kbytes, .maxlen = sizeof(sysctl_overcommit_kbytes), .mode = 0644, .proc_handler = overcommit_kbytes_handler, }, { .procname = "user_reserve_kbytes", .data = &sysctl_user_reserve_kbytes, .maxlen = sizeof(sysctl_user_reserve_kbytes), .mode = 0644, .proc_handler = proc_doulongvec_minmax, }, { .procname = "admin_reserve_kbytes", .data = &sysctl_admin_reserve_kbytes, .maxlen = sizeof(sysctl_admin_reserve_kbytes), .mode = 0644, .proc_handler = proc_doulongvec_minmax, }, };
static int __init init_vm_util_sysctls(void) { register_sysctl_init("vm", util_sysctl_table); return 0; }
subsys_initcall(init_vm_util_sysctls); #endif
|