slab分配器基本原理:


       slab最初是在Solaris 2.4中引入linux操作系统的,用于解决内碎片问题。程序经常需要创建一些数据结构,比如进程描述符task_struct,内存描述符mm_struct等。slab分配器把这些需要分配的小块内存区作为对象,类似面向对象的思想。每一类对象分配一个cache,cache有一个或多个slab组成,slab由一个或多个物理页面组成。需要分配对象的时候从slab中空闲的对象取,用完了再放回slab中,而不是释放给物理页分配器,这样下次用的时候就不用重新初始化了,实现了对象的复用。

[cpp]  view plain copy
  1. struct kmem_cache_s {  
  2. /* 1) per-cpu data, touched during every alloc/free */  
  3.     struct array_cache  *array[NR_CPUS];  
  4.     unsigned int        batchcount;  
  5.     unsigned int        limit;  
  6. /* 2) touched by every alloc & free from the backend */  
  7.     struct kmem_list3   lists;  
  8.     /* NUMA: kmem_3list_t   *nodelists[MAX_NUMNODES] */  
  9.     unsigned int        objsize;  
  10.     unsigned int        flags;  /* constant flags */  
  11.     unsigned int        num;    /* # of objs per slab */  
  12.     unsigned int        free_limit; /* upper limit of objects in the lists */  
  13.     spinlock_t      spinlock;  
  14.   
  15. /* 3) cache_grow/shrink */  
  16.     /* order of pgs per slab (2^n) */  
  17.     unsigned int        gfporder;  
  18.   
  19.     /* force GFP flags, e.g. GFP_DMA */  
  20.     unsigned int        gfpflags;  
  21.   
  22.     size_t          colour;     /* cache colouring range */  
  23.     unsigned int        colour_off; /* colour offset */  
  24.     unsigned int        colour_next;    /* cache colouring */  
  25.     kmem_cache_t        *slabp_cache;  
  26.     unsigned int        slab_size;  
  27.     unsigned int        dflags;     /* dynamic flags */  
  28.   
  29.     /* constructor func */  
  30.     void (*ctor)(void *, kmem_cache_t *, unsigned long);  
  31.   
  32.     /* de-constructor func */  
  33.     void (*dtor)(void *, kmem_cache_t *, unsigned long);  
  34.   
  35. /* 4) cache creation/removal */  
  36.     const char      *name;  
  37.     struct list_head    next;  
  38.   
  39. /* 5) statistics */  
  40. #if STATS  
  41.     unsigned long       num_active;  
  42.     unsigned long       num_allocations;  
  43.     unsigned long       high_mark;  
  44.     unsigned long       grown;  
  45.     unsigned long       reaped;  
  46.     unsigned long       errors;  
  47.     unsigned long       max_freeable;  
  48.     unsigned long       node_allocs;  
  49.     atomic_t        allochit;  
  50.     atomic_t        allocmiss;  
  51.     atomic_t        freehit;  
  52.     atomic_t        freemiss;  
  53. #endif  
  54. #if DEBUG  
  55.     int         dbghead;  
  56.     int         reallen;  
  57. #endif  
  58. };  

如图,每一个cache用高速缓存描述符struct kmem_cache_t来描述,所有的cache链接在一个以cache_chain为链表头的链表上。其中ctor,dtor 分别是构造和析构函数,在调用cache_grow()分配slab块之后,会调用ctor为每个对象初始化,有点面向对象的思想。colour是颜色的粒度的最大值,colour_off是颜色粒度的单位,colour_next下一个被分配的slab使用的颜色。

[cpp]  view plain copy
  1. struct kmem_list3 {  
  2.     struct list_head    slabs_partial;  /* partial list first, better asm code */  
  3.     struct list_head    slabs_full;  
  4.     struct list_head    slabs_free;  
  5.     unsigned long   free_objects;  
  6.     int     free_touched;  
  7.     unsigned long   next_reap;  
  8.     struct array_cache  *shared;  
  9. };  
struct kmem_cache_t有一个叫做struct kmem_list3的字段,里边保存了一些链表头,有list3,也就是slabs_full(没有空闲对象),slabs_partial(有部分空闲对象),slabs_free(只包含空闲对象)。还有一个struct array_cache *share字段,指向一个共享本地高速缓存。
[cpp]  view plain copy
  1. struct array_cache {  
  2.     unsigned int avail;          
  3.     unsigned int limit;  
  4.     unsigned int batchcount;  
  5.     unsigned int touched;  
  6. };  
struct kmem_cache_t的struct array_cache *array[NR_CPUS];字段指向了CPU的本地高速缓存。avail当前空闲对象的位置,limit是空闲对象的最大值。batchcount一次要填充给数组的对象数,或一次要释放的对象数。至于touched,如果本地高速缓存最近被使用过则touched置1。

[cpp]  view plain copy
  1. <span style="font-size:24px;">struct cache_sizes {  
  2.     size_t       cs_size;  
  3.     kmem_cache_t    *cs_cachep;  
  4.     kmem_cache_t    *cs_dmacachep;  
  5. };</span>  
这个是malloc_sizes表的类型,即普通高速缓存,cs_size指出了cache每次分配的内存区的大小,为32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072。这里一共13种几何分布的内存区,每种有两个高速缓存,一个用于ISA DMA分配,另一个适用于常规分配。

[cpp]  view plain copy
  1. struct slab {  
  2.     struct list_head    list;  
  3.     unsigned long       colouroff;  
  4.     void            *s_mem;     /* including colour offset */  
  5.     unsigned int        inuse;      /* num of objs active in slab */  
  6.     kmem_bufctl_t       free;  
  7. };  

这个就是SLAB描述符了,所有的slab描述符连在头节点为slabs_full,slabs_partial,slabs_free的双向链表上,colouroff为第一个对象的偏移。s_mem是slab中第一个对象的地址。这里的kmem_bufctl_t是对象描述符,在i386体系中是unsigned short类型,占2个字节。free保存下一个空闲对象的下标。

普通和专用高速缓存:

[cpp]  view plain copy
  1. static kmem_cache_t cache_cache = {  
  2.     .lists      = LIST3_INIT(cache_cache.lists),  
  3.     .batchcount = 1,  
  4.     .limit      = BOOT_CPUCACHE_ENTRIES,  
  5.     .objsize    = sizeof(kmem_cache_t),  
  6.     .flags      = SLAB_NO_REAP,  
  7.     .spinlock   = SPIN_LOCK_UNLOCKED,  
  8.     .name       = "kmem_cache",  
  9. #if DEBUG  
  10.     .reallen    = sizeof(kmem_cache_t),  
  11. #endif  
  12. };  
第一个高速缓存叫kmem_cache,存放在cache_cache变量中,它包含由内核其他部分使用的高速缓存的高速缓存描述符。上边说的那个叫malloc_sizes的表保存了指向这26个高速缓存的描述符。专用高速缓存使用kmem_cache_create()创建的,下一篇文档会专门介绍。

slab着色基本原理:

CPU访问内存时,会把一些经常访问的缓存到cache中。缓存到哪个cache line是靠低地址,所以即使距离很远的两块内存,只要低地址相同还是会被缓存到相同的cache line中,如下图。这样会经常造成cache miss,cache line被不停的换入,换出,导致cache颠簸。


如果在同一个cache的不同slab前加上不同的偏移,这样他们的低地址就不同了,可以被缓存在不同的cache line。

但是这样也存在问题,比如colour是3,那么第一个对象的偏移是0*32,第二个是1*32,第三个是2*32,可是第四个的时候又循环回来0*32,所以着色是有局限性的,当slab比较少的时候可以,比较多了着色就不起什么作用了,这是slab的缺陷之一。

Logo

更多推荐