linux 源代码分析10:slab通用长度内存分配
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" 转载请保留原文链接及作者。