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

法律声明linux 3.4.10 内核内存管理源代码分析》系列文章由机器人[email protected])发表于http://blog.csdn.net/ancjf,文章遵循GPL协议。欢迎转载,转载请注明作者和此条款。

slab通用长度内存分配==================

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

         100struct cache_sizes {

101        size_t                  cs_size;      //长度

102         struct kmem_cache       *cs_cachep;       //通用slab缓存

103 #ifdefCONFIG_ZONE_DMA

104         struct kmem_cache       *cs_dmacachep;        //dma专用缓存

105 #endif

106 };

         另外定义了一个结构struct cache_names,专门用于对长度缓存命名。在mm/slab.c定义如下:

         563struct cache_names {

        564         char *name; //通用缓存的名称

        565         char *name_dma; //dma缓存的名称

        566 };

         在系统中定义了一个全局变量malloc_sizes,是一个长度缓存数组,在mm/slab.c定义如下:

554 structcache_sizes malloc_sizes[] = {

555 #defineCACHE(x) { .cs_size = (x) },

        556 #include <linux/kmalloc_sizes.h>

        557         CACHE(ULONG_MAX)

558 #undef CACHE

559 };

         这里采用了一种宏技术,就是在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中定义的最大的长度,返回的将是空空指针。

猜你喜欢

转载自blog.csdn.net/ancjf/article/details/9077055