在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,用于长度缓存的命名。
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函数在mm/slab.c中实现,代码如下:
3899 void *__kmalloc(size_t size, gfp_tflags)
3900 {
3901 return __do_kmalloc(size, flags, NULL);
3902 }
直接是对__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函数中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中定义的最大的长度,返回的将是空空指针。