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申请内存源码分析

内核空间内存申请函数kmalloc kzalloc vmalloc的区别

Linux内核下内存空间的申请

内核申请内存的方法

kmalloc/kzalloc/vmalloc/malloc和get_free_page的区别

内核中申请内存的函数