码迷,mamicode.com
首页 > 其他好文 > 详细

Kernel那些事儿之内存管理(9) --- Slab(下)

时间:2015-07-27 16:34:32      阅读:121      评论:0      收藏:0      [点我收藏+]

标签:linux kernel   内存管理   

有了Slab的数据结构和操作方法之后,就可以创建一个指定大小的cache,然后在这个cache中申请和释放object。这个做法很适合这种应用场景:频繁地使用一固定大小的内存空间。

如果只是偶尔使用某个大小的内存空间,为此新建一个cache就有点得不偿失。针对于这种应用场景,内核提供了一系列 general caches。今天我们就来看一看这些general caches是怎么工作的。


其思想很简单:这些general caches会在系统初始化时提前创建好,且其大小按照2的幂次方由小到大分布,即 2^5, 2^6, ..., 2^25,其上限由 KMALLOC_MAX_SIZE 决定。当申请某一大小 size 的内存空间时,只需找出比size大的最小的general cache,然后从中分配object就可以了。这样可以保证内碎片总是小于50%。


1. malloc_sizes

那这些已经创建好的general caches放在哪里了呢?它们放在了数组 malloc_sizes 中。

 640 struct cache_sizes malloc_sizes[] = {
 641 #define CACHE(x) { .cs_size = (x) },
 642 #include <linux/kmalloc_sizes.h>
 643     CACHE(ULONG_MAX)
 644 #undef CACHE
 645 };

这个数组不大容易看清楚。我们一点点来。


首先,该数组中成员变量的类型是 struct cache_sizes。

 19 struct cache_sizes {
 20     size_t          cs_size;
 21     struct kmem_cache   *cs_cachep;
 22 #ifdef CONFIG_ZONE_DMA
 23     struct kmem_cache   *cs_dmacachep;
 24 #endif
 25 };

cs_size 指定了这一项中cache的object大小。每一项都包含两个cache,一个提供用于DMA操作的内存,一个提供用于正常操作的内存。


其次,数组 malloc_sizes 中包含了一个头文件,这个头文件就定义了该数组都有哪些 size 的general cache。该头文件内容为:

  1 #if (PAGE_SIZE == 4096)
  2     CACHE(32)
  3 #endif
  4     CACHE(64)
  5 #if L1_CACHE_BYTES < 64
  6     CACHE(96)
  7 #endif
  8     CACHE(128)
  9 #if L1_CACHE_BYTES < 128
 10     CACHE(192)
 11 #endif
 12     CACHE(256)
 13     CACHE(512)
 14     CACHE(1024)
 15     CACHE(2048)
 16     CACHE(4096)
 17     CACHE(8192)
 18     CACHE(16384)
 19     CACHE(32768)
 20     CACHE(65536)
 21     CACHE(131072)
 22 #if KMALLOC_MAX_SIZE >= 262144
 23     CACHE(262144)
 24 #endif
 25 #if KMALLOC_MAX_SIZE >= 524288
 26     CACHE(524288)
 27 #endif
 28 #if KMALLOC_MAX_SIZE >= 1048576
 29     CACHE(1048576)
 30 #endif
 31 #if KMALLOC_MAX_SIZE >= 2097152
 32     CACHE(2097152)
 33 #endif
 34 #if KMALLOC_MAX_SIZE >= 4194304
 35     CACHE(4194304)
 36 #endif
 37 #if KMALLOC_MAX_SIZE >= 8388608
 38     CACHE(8388608)
 39 #endif
 40 #if KMALLOC_MAX_SIZE >= 16777216
 41     CACHE(16777216)
 42 #endif
 43 #if KMALLOC_MAX_SIZE >= 33554432
 44     CACHE(33554432)
 45 #endif


最后,数组 malloc_sizes 的最后定义了一个大小为 ULONG_MAX 的general cache。这个cache在系统初始化的时候不会被创建,而是赋值为NULL。这样,如果有人申请了大于头文件 kmalloc_sizes.h 中定义的最大长度的内存时,就会得到NULL。


2. kmalloc/kfree

这些general caches对外提供的API就是kmalloc和kfree。

 31 static inline void *kmalloc(size_t size, gfp_t flags)
 32 {
 33     if (__builtin_constant_p(size)) {
 34         int i = 0;
 35
 36         if (!size)
 37             return ZERO_SIZE_PTR;
 38
 39 #define CACHE(x)  40         if (size <= x)  41             goto found;  42         else  43             i++;
 44 #include "kmalloc_sizes.h"
 45 #undef CACHE
 46         {
 47             extern void __you_cannot_kmalloc_that_much(void);
 48             __you_cannot_kmalloc_that_much();
 49         }
 50 found:
 51 #ifdef CONFIG_ZONE_DMA
 52         if (flags & GFP_DMA)
 53             return kmem_cache_alloc(malloc_sizes[i].cs_dmacachep,
 54                         flags);
 55 #endif
 56         return kmem_cache_alloc(malloc_sizes[i].cs_cachep, flags);
 57     }
 58     return __kmalloc(size, flags);
 59 }

kmalloc首先看一下参数中的size是不是一个常量。这种情况下,要使用的general cache在编译阶段就可以确定了,这对性能很有好处。


如果size不是常量,会最终调用到函数 __do_kmalloc()。

3714 static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
3715                       void *caller)
3716 {
3717     struct kmem_cache *cachep;

3724     cachep = __find_general_cachep(size, flags);
3725     if (unlikely(ZERO_OR_NULL_PTR(cachep)))
3726         return cachep;
3727     return __cache_alloc(cachep, flags, caller);
3728 }

该函数利用 __find_general_cachep() 找到合适的general cache,然后调用我们之前讲过的__cache_alloc()来从找到的cache中分配一个object出来。


 765 static inline struct kmem_cache *__find_general_cachep(size_t size,
 766                             gfp_t gfpflags)
 767 {
 768     struct cache_sizes *csizep = malloc_sizes;
 
 777     if (!size)
 778         return ZERO_SIZE_PTR;
 779
 780     while (size > csizep->cs_size)
 781         csizep++;
 782
 783     /*
 784      * Really subtle: The last entry with cs->cs_size==ULONG_MAX
 785      * has cs_{dma,}cachep==NULL. Thus no special case
 786      * for large kmalloc calls required.
 787      */
 788 #ifdef CONFIG_ZONE_DMA
 789     if (unlikely(gfpflags & GFP_DMA))
 790         return csizep->cs_dmacachep;
 791 #endif
 792     return csizep->cs_cachep;
 793 }

该函数根据size来找出合适的general cache。

780 ~ 781意思很明显,要找到比size大的最小的general cache。


3780 void kfree(const void *objp)
3781 {
3782     struct kmem_cache *c;
3783     unsigned long flags;
3784
3785     if (unlikely(ZERO_OR_NULL_PTR(objp)))
3786         return;
3787     local_irq_save(flags);
3788     kfree_debugcheck(objp);
3789     c = virt_to_cache(objp);
3790     debug_check_no_locks_freed(objp, obj_size(c));
3791     __cache_free(c, (void *)objp);
3792     local_irq_restore(flags);
3793 }

3789行根据object的地址,找出其对应的cache的地址。这一点并不难,因为cache的地址保存在了object所在页面的页描述符中的lru.next中了。


3791行调用我们之前讲过的__cache_free()来把object释放回前面找到的cache中。


本文出自 “内核部落格” 博客,请务必保留此出处http://richardguo.blog.51cto.com/9343720/1678760

Kernel那些事儿之内存管理(9) --- Slab(下)

标签:linux kernel   内存管理   

原文地址:http://richardguo.blog.51cto.com/9343720/1678760

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!