kmem_cache 作为一个基本的 allocator 组件,这里可以把他理解为用于某个特定大小的内存对象池。
Linux 系统中本身也存在很多这样的内存池,包括分配struct kmem_cache 和struct kmem_cache_node对象,也是在单独的对象内存池里面分配的。
当然系统里面也根据用途和大小分配了一些通用的对象内存池,参考:
struct kmem_cache *
kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1] __ro_after_init =
{ /* initialization for https://llvm.org/pr42570 */ };
EXPORT_SYMBOL(kmalloc_caches);
所以系统中存在各种各样的kmem_cache内存池,kmalloc这样的函数,就是在这些通用的内存池里面分配内存。
static __always_inline
void *__do_kmalloc_node(size_t size, gfp_t flags, int node,
unsigned long caller)
{
struct kmem_cache *s;
//...
s = kmalloc_slab(size, flags, caller); // 找到对应的kmem_cache
ret = slab_alloc_node(s, NULL, flags, node, caller, size); //在kmem_cache池里进行分配
//....
return ret;
}
可以通过如下命令查看系统中的内存池:
# cat /proc/slabinfo
slabinfo - version: 2.1
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
ext4_groupinfo_4k 22 22 184 22 1 : tunables 0 0 0 : slabdata 1 1 0
p9_req_t 0 0 160 25 1 : tunables 0 0 0 : slabdata 0 0 0
//......
kmalloc-8k 20 20 8192 4 8 : tunables 0 0 0 : slabdata 5 5 0
kmalloc-4k 56 56 4096 8 8 : tunables 0 0 0 : slabdata 7 7 0
kmalloc-2k 160 160 2048 16 8 : tunables 0 0 0 : slabdata 10 10 0
kmalloc-1k 544 544 1024 32 8 : tunables 0 0 0 : slabdata 17 17 0
kmalloc-512 640 640 512 32 4 : tunables 0 0 0 : slabdata 20 20 0
kmalloc-256 736 736 256 32 2 : tunables 0 0 0 : slabdata 23 23 0
kmalloc-192 1575 1575 192 21 1 : tunables 0 0 0 : slabdata 75 75 0
kmalloc-128 512 512 128 32 1 : tunables 0 0 0 : slabdata 16 16 0
kmalloc-96 630 630 96 42 1 : tunables 0 0 0 : slabdata 15 15 0
kmalloc-64 1344 1344 64 64 1 : tunables 0 0 0 : slabdata 21 21 0
kmalloc-32 1408 1408 32 128 1 : tunables 0 0 0 : slabdata 11 11 0
kmalloc-16 2816 2816 16 256 1 : tunables 0 0 0 : slabdata 11 11 0
kmalloc-8 5120 5120 8 512 1 : tunables 0 0 0 : slabdata 10 10 0
kmem_cache_node 320 320 64 64 1 : tunables 0 0 0 : slabdata 5 5 0
kmem_cache 256 256 256 32 2 : tunables 0 0 0 : slabdata 8 8 0
如果kernel中的驱动,有特殊用途,也可以创建自己的内存池。
可以参考文件 arch/arm64/mm/pgd.c
static struct kmem_cache *pgd_cache __ro_after_init;
pgd_t *pgd_alloc(struct mm_struct *mm)
{
gfp_t gfp = GFP_PGTABLE_USER;
if (pgdir_is_page_size())
return (pgd_t *)__get_free_page(gfp);
else
return kmem_cache_alloc(pgd_cache, gfp);
}
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
if (pgdir_is_page_size())
free_page((unsigned long)pgd);
else
kmem_cache_free(pgd_cache, pgd);
}
void __init pgtable_cache_init(void)
{
//.....
/*
* Naturally aligned pgds required by the architecture.
*/
pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_SIZE,
SLAB_PANIC, NULL);
}
kernel/fork.c
static struct kmem_cache *thread_stack_cache;
static void thread_stack_free_rcu(struct rcu_head *rh)
{
kmem_cache_free(thread_stack_cache, rh);
}
static void thread_stack_delayed_free(struct task_struct *tsk)
{
struct rcu_head *rh = tsk->stack;
call_rcu(rh, thread_stack_free_rcu);
}
static int alloc_thread_stack_node(struct task_struct *tsk, int node)
{
unsigned long *stack;
stack = kmem_cache_alloc_node(thread_stack_cache, THREADINFO_GFP, node);
stack = kasan_reset_tag(stack);
tsk->stack = stack;
return stack ? 0 : -ENOMEM;
}
void thread_stack_cache_init(void)
{
thread_stack_cache = kmem_cache_create_usercopy("thread_stack",
THREAD_SIZE, THREAD_SIZE, 0, 0,
THREAD_SIZE, NULL);
BUG_ON(thread_stack_cache == NULL);
}
上面两个例子唯一的区别是不是要使用到usercopy,如果使用到usercopy的话,就用kmem_cache_create_usercopy。 这样在copyfromuser的时候,位了避免userspace的攻击,这个时候就可以通过user 的size 和offset 来检查这个copy是不是合法,不合法就可以拒绝copy。
参考函数 __check_heap_object.
在使能 CONFIG_SLUB_DEBUG 这之后,可以对每个pool的分配情况进行查询,如下:
# cat /sys/kernel/debug/slab/task_struct/alloc_traces
102 copy_process+0x144/0x141c age=39045/1797787/1833528 pid=2 cpus=0-1
kmem_cache_alloc_node+0x20c/0x264
copy_process+0x144/0x141c
kernel_clone+0x68/0x368
kernel_thread+0x80/0xb4
kthreadd+0x144/0x1c4
ret_from_fork+0x10/0x20
7 copy_process+0x144/0x141c age=1833521/1833521/1833522 pid=1 cpus=0
kmem_cache_alloc_node+0x20c/0x264
copy_process+0x144/0x141c
fork_idle+0x90/0x110
idle_threads_init+0xcc/0x104
smp_init+0x14/0x8c
kernel_init_freeable+0x100/0x290
kernel_init+0x20/0x1d8
ret_from_fork+0x10/0x20
4 copy_process+0x144/0x141c age=5/1371620/1828871 pid=1-149 cpus=6-7
kmem_cache_alloc_node+0x20c/0x264
copy_process+0x144/0x141c
kernel_clone+0x68/0x368
__do_sys_clone+0x74/0xb0
__arm64_sys_clone+0x20/0x2c
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0x40/0xe0
do_el0_svc+0x1c/0x28
el0_svc+0x34/0xd8
el0t_64_sync_handler+0x120/0x12c
el0t_64_sync+0x190/0x194
1 copy_process+0x144/0x141c age=1833529 pid=0 cpus=0
kmem_cache_alloc_node+0x20c/0x264
copy_process+0x144/0x141c
kernel_clone+0x68/0x368
user_mode_thread+0x70/0xa4
rest_init+0x30/0xe4
start_kernel+0x50c/0x614
__primary_switched+0x80/0x88
1 copy_process+0x144/0x141c age=1833529 pid=0 cpus=0
kmem_cache_alloc_node+0x20c/0x264
copy_process+0x144/0x141c
kernel_clone+0x68/0x368
kernel_thread+0x80/0xb4
rest_init+0xa0/0xe4
start_kernel+0x50c/0x614
__primary_switched+0x80/0x88
# cat /sys/kernel/debug/slab/task_struct/free_traces
104 <not-available> age=4297136502 pid=0 cpus=0
11 free_task+0x54/0x80 age=410662/1767494/2243661 pid=0-126 cpus=0-1,4-6
kmem_cache_free+0x1fc/0x2a0
free_task+0x54/0x80
__put_task_struct+0x100/0x154
delayed_put_task_struct+0x7c/0xa8
rcu_core+0x25c/0x654
rcu_core_si+0x10/0x1c
handle_softirqs+0x108/0x24c
__do_softirq+0x14/0x20
Comments !