linux 源代码分析10:slab通用长度内存分配

  1. slab通用长度内存分配
    1. struct cache_sizes 结构
    2. cache_names 结构
    3. malloc_sizes 变量
    4. Kmalloc函数
    5. __kmalloc函数
    6. __do_kmalloc函数
    7. __find_general_cachep函数

slab通用长度内存分配

在slab分配内存并不是总是要先创建一个slab缓存,然后创建的缓存中分配内存。在slab中创建了预先定义好的一些通用缓存,使得我们直接按长度就可以进行内存分配。我们把这些缓存叫做长度缓存。对长度缓存专门定义了一个结构struct cache_sizes。在文件中include/linux/slab_def.h定义如下:

struct cache_sizes 结构

struct cache_sizes {
    size_t         cs_size;      //长度
    struct kmem_cache       *cs_cachep;       //通用slab缓存
    #ifdefCONFIG_ZONE_DMA
        struct kmem_cache       *cs_dmacachep;        //dma专用缓存
    #endif
};

cache_names 结构

struct cache_names 结构专门用于对长度缓存命名。在mm/slab.c定义如下:
struct cache_names {
    char *name; //通用缓存的名称
    char *name_dma; //dma缓存的名称
};

malloc_sizes 变量

在系统中定义了一个全局变量malloc_sizes,是一个长度缓存数组,在mm/slab.c定义如下:
structcache_sizes malloc_sizes[] = {
    #defineCACHE(x) { .cs_size = (x) },
    #include <linux/kmalloc_sizes.h>
    CACHE(ULONG_MAX)
    #undef CACHE
};
这里采用了一种宏技术,就是在linux/kmalloc_sizes.h声明了一系列的宏,如CACHE(32),CACHE(64),然后在555行对这个宏进行定义,556行包含这个头文件,557行加上CACHE(ULONG_MAX),用于长度缓存的结尾。558行取消宏CACHE的定义。这样做实际的效果和
structcache_sizes malloc_sizes[]={
{.cs_size = 32},
{.cs_size = 64},
…..
{.cs_size = ULONG_MAX }
};
是一样的,但提供了代码的通用性,后面还有些代码也使用了这项技术。
另外采用同样的方法定义了一个全局数组cache_names,用于长度缓存的命名。

Kmalloc函数

slab通用长度内存分配函数是kmalloc,在include/linux/slab_def.h中定义,代码如下:
128 static __always_inline void *kmalloc(size_tsize, gfp_t flags)
129 {
130        struct kmem_cache *cachep;
131        void *ret;
132
133        if (__builtin_constant_p(size)) {
134        int i = 0;
135
136        if (!size)
137                return ZERO_SIZE_PTR;
138
139 #define CACHE(x) \
140        if (size <= x) \
141                goto found; \
142        else \
143                i++;
144 #include <linux/kmalloc_sizes.h>
145 #undef CACHE
146        return NULL;
147 found:
148 #ifdef CONFIG_ZONE_DMA
149        if (flags & GFP_DMA)
150                cachep =malloc_sizes[i].cs_dmacachep;
151        else
152 #endif
153                cachep =malloc_sizes[i].cs_cachep;
154
155        ret =kmem_cache_alloc_trace(size, cachep, flags);
156
157        return ret;
158        }
159        return __kmalloc(size, flags);
160 }

__builtin_constant_p是一个用来判断一个值是否为编译期常数的gcc内存函数,是常数返回1,否则返回0。
133-158是参数size为编译期参数的处理代码。
139-143定义了宏CACHE,144行包含了头文件linux/kmalloc_sizes.h,在头文件linux/kmalloc_sizes.h中包含了一系列的CACHE定义,展开后就是一堆
if(if (size <= n) \
    gotofound; \
else\
    i++;
这样的代码,目的就是找到第零个满足size<= n的长度缓存的在malloc_sizes数组的下标。
148-153行是求slab缓存的代码。求出slab缓存后,就可以调用kmem_cache_alloc_trace函数进行内存分配了。
133-158这段代码gcc在编译期间会优化,优化的结果是133-154这段代码根本不用执行,因为在编译阶段编译器就可以求出cachep的值。
159行是size不是编译期常数的情况,直接调用__kmalloc函数进行分配。

__kmalloc函数

__kmalloc函数在mm/slab.c中实现,代码如下:
3899 void *__kmalloc(size_t size, gfp_tflags)
3900 {
3901        return __do_kmalloc(size, flags, NULL);
3902 }

直接是对__do_kmalloc的调用。

__do_kmalloc函数

__do_kmalloc函数在mm/slab.c中实现,代码如下:
3862 static __always_inline void*__do_kmalloc(size_t size, gfp_t flags,
3863                                  void*caller)
3864 {
3865        struct kmem_cache *cachep;
3866        void *ret;
3867
3868        /* If you want to save a few bytes .text space: replace
3869 * __ with kmem_.
3870 * Then kmalloc uses the uninlinedfunctions instead of the inline
3871          * functions.
3872 */
3873        cachep = __find_general_cachep(size, flags);
3874        if (unlikely(ZERO_OR_NULL_PTR(cachep)))
3875        return cachep;
3876        ret = __cache_alloc(cachep, flags, caller);
3877
3878        trace_kmalloc((unsignedlong) caller, ret,
3879              size,cachep->buffer_size, flags);
3880
3881        return ret;
3882 }

3873行调用__find_general_cachep查找缓存。
3874-3875是size为零或者返回指针为空的情况,如果size大于linux/kmalloc_sizes.h中定义的长度最大的缓存,__find_general_cachep会返回空指针。
3876调用__cache_alloc函数进行分配,__cache_alloc函数前面已经分析过。

__find_general_cachep函数

__find_general_cachep函数中mm/slab.c中实现,代码如下:
732 static inline struct kmem_cache*__find_general_cachep(size_t size,
733                                                gfp_t gfpflags)
734{
735        struct cache_sizes *csizep = malloc_sizes;
736
737#if DEBUG
738        /* This happens if someone tries to call
739         * kmem_cache_create(), or __kmalloc(), before
740         * the generic caches are initialized.
741         */
742        BUG_ON(malloc_sizes[INDEX_AC].cs_cachep == NULL);
743#endif
744        if (!size)
745        return ZERO_SIZE_PTR;
746
747        while (size > csizep->cs_size)
748                csizep++;
749
750        /*
751         * Really subtle: The last entry with cs->cs_size==ULONG_MAX
752         * has cs_{dma,}cachep==NULL. Thus no special case
753         * for large kmalloc calls required.
754         */
755#ifdef CONFIG_ZONE_DMA
756        if (unlikely(gfpflags & GFP_DMA))
757        returncsizep->cs_dmacachep;
758#endif
759        return csizep->cs_cachep;
760}

744行,如果长度为0的情况,返回ZERO_SIZE_PTR,
__find_general_cachep函数的关键代码是747-748两行,查找到长度大于等于size的第零个长度缓存。在malloc_sizes的定义中最后一个长度缓存是CACHE(ULONG_MAX),在初始化代码中可以看到,并没有创建这个缓存,也就是说这个缓存是空,这样如果我们要分配的长度大于在linux/kmalloc_sizes.h中定义的最大的长度,返回的将是空空指针。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 ancjf@163.com

文章标题:linux 源代码分析10:slab通用长度内存分配

本文作者:ancjf

发布时间:2013-06-09, 20:17:42

最后更新:2020-04-18, 15:58:01

原始链接:http://ancjf.com/2013/06/09/linux-3-4-10-%E5%86%85%E6%A0%B8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%9010-slab%E9%80%9A%E7%94%A8%E9%95%BF%E5%BA%A6%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏