Kmalloc申请内存源码分析
Posted Loopers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kmalloc申请内存源码分析相关的知识,希望对你有一定的参考价值。
再上一节了解了SLUB是如何申请一个object的,其中涉及了从当前的freelist申请,以及kmem_cache_cpu->partital链表申请,以及到最后的kmem_cache_cpu→node中申请,如果上述三个步骤都没有申请到的话,就会重新创建一个新的slab,然后设置好freelist的指针,返回object使用。
本节我们重点分析下Kmalloc的实现,其实在驱动中大家使用最多的就是用kmalloc申请内存,kmalloc申请的内存大小都普遍比较小,比较快,而且物理地址和虚拟地址是线性映射的,因为kmalloc拿到的内存是从noraml zone获取的。关于zone的知识我们后面有机会说。
直接上代码,去看下Kmalloc的实现。
* The @flags argument may be one of:
*
* %GFP_USER - Allocate memory on behalf of user. May sleep.
*
* %GFP_KERNEL - Allocate normal kernel ram. May sleep.
*
* %GFP_ATOMIC - Allocation will not sleep. May use emergency pools.
* For example, use this inside interrupt handlers.
*
* %GFP_HIGHUSER - Allocate pages from high memory.
*
* %GFP_NOIO - Do not do any I/O at all while trying to get memory.
*
* %GFP_NOFS - Do not make any fs calls while trying to get memory.
*
* %GFP_NOWAIT - Allocation will not sleep.
*
* %__GFP_THISNODE - Allocate node-local memory only.
static __always_inline void *kmalloc(size_t size, gfp_t flags)
if (__builtin_constant_p(size))
if (size > KMALLOC_MAX_CACHE_SIZE)
return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
if (!(flags & GFP_DMA))
unsigned int index = kmalloc_index(size);
if (!index)
return ZERO_SIZE_PTR;
return kmem_cache_alloc_trace(kmalloc_caches[index],
flags, size);
#endif
return __kmalloc(size, flags);
- 申请的时候会传递2个参数,第一个参数就是要申请的大小,第二个参数就是申请内存的一些flag,比如常见的GFP_KERNEL
- 大家也看下注释都有哪些flag,这些flag都代表啥意思,是否可以睡眠,是否是原子操作等
- __builtin_constant_p 是gcc的一个属性,我们不用理会,直接看_kmalloc的实现。
#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM)
#define GFP_NOIO (__GFP_RECLAIM)
#define GFP_NOFS (__GFP_RECLAIM | __GFP_IO)
#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
#define GFP_DMA __GFP_DMA
#define GFP_DMA32 __GFP_DMA32
- 大家常用的应该有GFP_KERNEL,它是由三个flag组成的。__GFP_RECLAIM=可回收,__GFP_IO=有IO的操作,有IO的操作就可能会导致sleep
- GFP_ATOMIC: 它也是有三个组成,__GFP_HIGH=高优先级等等
- 大家有兴趣可以看看Gfp.h文件的注释就清楚了。
void *__kmalloc(size_t size, gfp_t flags)
struct kmem_cache *s;
void *ret;
if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
return kmalloc_large(size, flags);
s = kmalloc_slab(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
ret = slab_alloc(s, flags, _RET_IP_);
trace_kmalloc(_RET_IP_, ret, size, s->size, flags);
kasan_kmalloc(s, ret, size, flags);
return ret;
- 如果size大于Kmalloc申请的最大size(4K),则调用kmalloc_large去申请。也就是申请太大的内存,就不用直接找我slab了,直接去找buddy拿吧
- 通过kmalloc_slab去获取对应大小的kmem_cache缓冲池
- 调用slab_alloc从对应的kmem_cache中去申请一个object,返回去即可。
- 所以说来说去,还是从slab去申请内存,里面的内存就和上一节的内存一样了。
如果size大于4K,每个平台,每个版本,每个slab的实现大小不一样。
static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
unsigned int order = get_order(size);
return kmalloc_order_trace(size, flags, order);
void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
void *ret;
struct page *page;
flags |= __GFP_COMP;
page = alloc_pages(flags, order);
ret = page ? page_address(page) : NULL;
kmemleak_alloc(ret, size, 1, flags);
kasan_kmalloc_large(ret, size, flags);
return ret;
很直接,如果大小太大,不用找我slab了,我是小内存申请分配器,反正我的内存也是从buddy拿的,你直接去找buddy拿对应order的页。
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
unsigned int index;
if (size <= 192)
if (!size)
return ZERO_SIZE_PTR;
index = size_index[size_index_elem(size)];
else
if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
WARN_ON(1);
return NULL;
index = fls(size - 1);
#ifdef CONFIG_ZONE_DMA
if (unlikely((flags & GFP_DMA)))
return kmalloc_dma_caches[index];
#endif
return kmalloc_caches[index];
- 根据size计算出一个index,此index就是kmalloc_caches数组下标,此数据中有对应很多个大小的kmalloc_cache缓冲池
- 如果申请的内存是从DMA ZONE去申请,就从kmalloc_dma_caches中去找对应的kmalloc_cache缓冲池
static void __init new_kmalloc_cache(int idx, slab_flags_t flags)
kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name,
kmalloc_info[idx].size, flags, 0,
kmalloc_info[idx].size);
kmalloc_caches数组是通过kmalloc_info数组里面的信息决定的。
const struct kmalloc_info_struct kmalloc_info[] __initconst =
NULL, 0, "kmalloc-96", 96,
"kmalloc-192", 192, "kmalloc-8", 8,
"kmalloc-16", 16, "kmalloc-32", 32,
"kmalloc-64", 64, "kmalloc-128", 128,
"kmalloc-256", 256, "kmalloc-512", 512,
"kmalloc-1024", 1024, "kmalloc-2048", 2048,
"kmalloc-4096", 4096, "kmalloc-8192", 8192,
"kmalloc-16384", 16384, "kmalloc-32768", 32768,
"kmalloc-65536", 65536, "kmalloc-131072", 131072,
"kmalloc-262144", 262144, "kmalloc-524288", 524288,
"kmalloc-1048576", 1048576, "kmalloc-2097152", 2097152,
"kmalloc-4194304", 4194304, "kmalloc-8388608", 8388608,
"kmalloc-16777216", 16777216, "kmalloc-33554432", 33554432,
"kmalloc-67108864", 67108864
;
可以看到,系统中专门为kmalloc申请了一系列大小的kmem_cache缓冲区,平常用到的大小都会有涉及的。
大家也可以去/proc/slabinfo下去查看对应的kmalloc信息
root:/ # cat /proc/slabinfo | grep "kmalloc"
kmalloc-8192 644 663 8704 3 8 : tunables 0 0 0 : slabdata 221 221 0
kmalloc-4096 2250 2254 4608 7 8 : tunables 0 0 0 : slabdata 322 322 0
kmalloc-2048 6767 6780 2560 12 8 : tunables 0 0 0 : slabdata 565 565 0
kmalloc-1024 2374 2436 1536 21 8 : tunables 0 0 0 : slabdata 116 116 0
kmalloc-512 7034 7296 1024 32 8 : tunables 0 0 0 : slabdata 228 228 0
kmalloc-256 17285 17640 768 21 4 : tunables 0 0 0 : slabdata 840 840 0
kmalloc-128 301624 303775 640 25 4 : tunables 0 0 0 : slabdata 12151 12151 0
以上是关于Kmalloc申请内存源码分析的主要内容,如果未能解决你的问题,请参考以下文章
内核空间内存申请函数kmalloc kzalloc vmalloc的区别