Chinaunix首页 | 论坛 | 博客
  • 博客访问: 457396
  • 博文数量: 64
  • 博客积分: 3271
  • 博客等级: 中校
  • 技术积分: 727
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-30 18:42
文章分类

全部博文(64)

文章存档

2013年(1)

2011年(19)

2010年(42)

2009年(2)

分类: LINUX

2010-04-24 15:38:37

简单的了解了一下slub的kmalloc和kfree.

start_kernel

               kmem_cache_init()

void __init kmem_cache_init(void)

{

       int i;

       int caches = 0;

 

       init_alloc_cpu();

       /* Able to allocate the per node structures */

       slab_state = PARTIAL;


static enum {

       DOWN,         /* No slab functionality available */

       PARTIAL,      /* kmem_cache_open() works but kmalloc does not */

       UP,         /* Everything works but does not show up in sysfs */

       SYSFS           /* Sysfs up */

} slab_state = DOWN;

 

设置完状态后然后创建kmem_cache;

/* Caches that are not of the two-to-the-power-of size */

       if (KMALLOC_MIN_SIZE <= 64) {

              create_kmalloc_cache(&kmalloc_caches[1],

                            "kmalloc-96", 96, GFP_KERNEL);

              caches++;

              create_kmalloc_cache(&kmalloc_caches[2],

                            "kmalloc-192", 192, GFP_KERNEL);

              caches++;

       }

 

       for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) {

              create_kmalloc_cache(&kmalloc_caches[i],

                     "kmalloc", 1 << i, GFP_KERNEL);

              caches++;

       }

slub.c中。

#ifndef ARCH_KMALLOC_MINALIGN

#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)

#endif

然后在slub_def.h

/*

 * Kmalloc subsystem.

 */

#if defined(ARCH_KMALLOC_MINALIGN) && ARCH_KMALLOC_MINALIGN > 8

#define KMALLOC_MIN_SIZE ARCH_KMALLOC_MINALIGN

#else

#define KMALLOC_MIN_SIZE 8

#endif

 

先判断是否小于64,在如上可知,KMALLOC_MIN_SIZE定义为8,所以创建了一个大小为96的,和一个大小为192kmem_cahce

extern struct kmem_cache kmalloc_caches[SLUB_PAGE_SHIFT];

然后进入for语句。

#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE)

#define SLUB_MAX_SIZE (2 * PAGE_SIZE)

#define SLUB_PAGE_SHIFT (PAGE_SHIFT + 2)

所以创建了从314位置大小分别为2^3,2^4….2^14大小mem_cache.注意到它们都是保存在数组kmalloc_cache[1-14]中的。即是它们是用于kmalloc的。

 

       接下来的代码由于KMALLOC_MIN_SIZE8 ,所以直到这里才会执行:

       slab_state = UP;

       /* Provide the correct kmalloc names now that the caches are up */

       for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++)

              kmalloc_caches[i]. name =

                     kasprintf(GFP_KERNEL, "kmalloc-%d", 1 << i);

为每个kamlloc_caches[3-13]分配姓名。

       kmem_size = sizeof(struct kmem_cache);

计算大小,并且赋值给全局静态变量。

 

至此初始化完成。

 

                   create_kmalloc_cache

kmem_cache_init中对它的调用如下:

create_kmalloc_cache(&kmalloc_caches[i],"kmalloc", 1 << i, GFP_KERNEL);

 

static struct kmem_cache *create_kmalloc_cache(struct kmem_cache *s,

              const char *name, int size, gfp_t gfp_flags)

{

       unsigned int flags = 0;

 

       if (gfp_flags & SLUB_DMA)

              flags = SLAB_CACHE_DMA;

       down_write(&slub_lock);

       if (!kmem_cache_open(s, gfp_flags, name, size, ARCH_KMALLOC_MINALIGN,

                                                        flags, NULL))

              goto panic;

 

       list_add(&s->list, &slab_caches);

       up_write(&slub_lock);

       if (sysfs_slab_add(s))

              goto panic;

       return s;

 

首先判断gfp_flags是否设置了SLUB_DMA位,如果设置,则将flags设置成SLAB_CACHE_DMA;

然后进入kmem_cache_open函数。之后再将上面传递来的s,kmalloc_caches[i]添加到链表slab_caches上。

   static LIST_HEAD(slab_caches);

然后调用sysfs_slab_adds)将kmalloc_caches[i]添加到sysfs中。

 

                        kmem_cache_open

static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags,

              const char *name, size_t size,

              size_t align, unsigned long flags,

              void (*ctor)(void *))

{

       memset(s, 0, kmem_size);

       s->name = name;

       s->ctor = ctor;

       s->objsize = size;

       s->align = align;

       s->flags = kmem_cache_flags(size, flags, name, ctor);

 

       if (!calculate_sizes(s, -1))

              goto error;

首先,将kmalloc_caches[i]0;然后设定名字。构造函数NULL,容纳的对象大小,对齐大小。并且将flag赋值给kmalloc_cahes[i]flags.

注意在

kmem_cache_open(s, gfp_flags, name, size, ARCH_KMALLOC_MINALIGN, flags, NULL)

的调用中,flags仅仅当为DMA时为SLAB_CACHE_DMA,其它情况全为0,即如果不是DMA,则s->flags0

    然后调用calculate_sizes,calculate_sizes() determines the order and the distribution of data within a slab object.

它主要完成以下的工作:

s-> = ;

s-> = ;

s-> = (order, );

s-> = ((), );

计算了对象数,对象的offset。对象size。可能还会设置flagsoffset

 

然后:

set_min_partial(s, ilog2(s->size));

       s->refcount = 1;

设置了s->min_partial,和引用计数。

       if (!init_kmem_cache_nodes(s, gfpflags & ~SLUB_DMA))

              goto error;

 

       if (alloc_kmem_cache_cpus(s, gfpflags & ~SLUB_DMA))

              return 1;

       free_kmem_cache_nodes(s);

 

之后三个函数,一个一个看。

                             init_kmem_cache_nodes

在非#ifdef CONFIG_NUMA系统上。

static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t gfpflags)

{

       init_kmem_cache_node(&s->local_node, s);

       return 1;

}

static void

init_kmem_cache_node(struct kmem_cache_node *n, struct kmem_cache *s)

{

       n->nr_partial = 0;

       spin_lock_init(&n->list_lock);

       INIT_LIST_HEAD(&n->partial);

}

 

可以看到,仅仅是简单的赋值,初始化lock,初始化partial链表。

                             alloc_kmem_cache_cpus

对于非对于多处理器即未定义#ifdef CONFIG_SMP

static inline int alloc_kmem_cache_cpus(struct kmem_cache *s, gfp_t flags)

{

       init_kmem_cache_cpu(s, &s->cpu_slab);

       return 1;

}

static void init_kmem_cache_cpu(struct kmem_cache *s,

                     struct kmem_cache_cpu *c)

{

       c->page = NULL;

       c->freelist = NULL;

       c->node = 0;

       c->offset = s->offset / sizeof(void *);

       c->objsize = s->objsize;

}

slub_def.h中有如下定义:

#ifdef CONFIG_SMP

       struct kmem_cache_cpu *cpu_slab[NR_CPUS];

#else

       struct kmem_cache_cpu cpu_slab;

free_kmem_cache_nodes

static void free_kmem_cache_nodes(struct kmem_cache *s)

{

}

 

               kmalloc

tatic __always_inline void *kmalloc(size_t size, gfp_t flags)

{

       void *ret;

 

       if (__builtin_constant_p(size)) {

              if (size > SLUB_MAX_SIZE)

                     return kmalloc_large(size, flags);

首先检查size是否为builtin,如果是,比如kalloc(100,KERNEL);

判断是否大于SLUB_MAX_SIZE;

#define SLUB_MAX_SIZE (2 * PAGE_SIZE)

即分配超过2page的大小则使用kmalloc_large();

              if (!(flags & SLUB_DMA)) {

                     struct kmem_cache *s = kmalloc_slab(size);

                     if (!s)

                            return ZERO_SIZE_PTR;

                     ret = kmem_cache_alloc_notrace(s, flags);

                     trace_kmalloc(_THIS_IP_, ret, size, s->size, flags);

                     return ret;

              }

       }

       return __kmalloc(size, flags);

}

 

如果flags不是SLUB_DMA,调用kmalloc_slab(size)

static __always_inline struct kmem_cache *kmalloc_slab(size_t size)

{

       int index = kmalloc_index(size);

       if (index == 0)

              return NULL;

       return &kmalloc_caches[index];

}

 

即根据size的大小,对应的区间来找前面注册的kmem_cacheskmalloc_caches[i];比如是size=100;index=7,所以kmalloc_slab返回kmalloc_caches[7];

然后:

                   kmem_cache_alloc_notrace

static __always_inline void *

kmem_cache_alloc_notrace(struct kmem_cache *s, gfp_t gfpflags)

{

       return kmem_cache_alloc(s, gfpflags);

}

void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)

{

       void *ret = slab_alloc(s, gfpflags, -1, _RET_IP_);

       trace_kmem_cache_alloc(_RET_IP_, ret, s->objsize, s->size, gfpflags);

       return ret;

}

进入真正的slab分配函数:

static __always_inline void *slab_alloc(struct kmem_cache *s,

              gfp_t gfpflags, int node, unsigned long addr)

{

       void **object;

       struct kmem_cache_cpu *c;

       unsigned long flags;

       unsigned int objsize;

       lockdep_trace_alloc(gfpflags);

       might_sleep_if(gfpflags & __GFP_WAIT);

       if (should_failslab(s->objsize, gfpflags))

              return NULL;

       local_irq_save(flags);

       c = get_cpu_slab(s, smp_processor_id());

       objsize = c->objsize;

做一些检查,关中断,取出per_cpustruct kmem_cache_cpu cpu_slab;alloc_kmem_caches中已经初始化过。然后取出objsize;

if (unlikely(!c->freelist || !node_match(c, node)))

 

              object = __slab_alloc(s, gfpflags, node, addr, c);

       else {

              object = c->freelist;

              c->freelist = object[c->offset];

              stat(c, ALLOC_FASTPATH);

       }

       local_irq_restore(flags);

       if (unlikely((gfpflags & __GFP_ZERO) && object))

              memset(object, 0, objsize);

       return object;

}

如果freelist空闲队列木有可分配的了,或者node_match(c,node)注意,这个node只在NUMA中有用。

   则调用__slab_alloc进行分配。

否则:

c->offset = s->offset / sizeof(void *);

s->offset=0;

结合人代码来看:

struct kmem_cache_cpu {

       void **freelist;       /* Pointer to first free per cpu object */

       struct page *page;   /* The slab from which we are allocating */

       int node;         /* The node of the page (or -1 for debug) */

       unsigned int offset; /* Freepointer offset (in word units) */

       unsigned int objsize;      /* Size of an object (from kmem_cache) */

};

freelist指向第一个空闲的object.它相当于一个指针数组,所以取出指针以后,将它指向下一个object的位置。

   然后恢复中断。如果设置了清0标志,则将这个object0

                        __slab_alloc

如果c->freelist为空,则会执行object = __slab_alloc(s, gfpflags, node, addr, c);

static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,

                       unsigned long addr, struct kmem_cache_cpu *c)

{

       void **object;

       struct page *new;

       /* We handle __GFP_ZERO in the caller */

       gfpflags &= ~__GFP_ZERO;

       if (!c->page)

              goto new_slab;

       slab_lock(c->page);

       if (unlikely(!node_match(c, node)))

              goto another_slab;

       stat(c, ALLOC_REFILL);

清除了GFP_ZERO标志,因为在上面处理了。如果当前cpupage为空,即未映射到内在页。则跳转到new_slab去执行。如果不match(c,node)会跳转到another_slab,由于非num中,始终为1,所以不会跳转

先看new_slab.

new_slab:

       new = get_partial(s, gfpflags, node);

       if (new) {

              c->page = new;

              stat(c, ALLOC_FROM_PARTIAL);

              goto load_freelist;

       }

先调用get_partial取出部分使用的page,赋值给new;如果存在,则c->page=new;然后跳转到load_freelist去。

若没有找到,即new=NULL;

if (gfpflags & __GFP_WAIT)

              local_irq_enable();

       new = new_slab(s, gfpflags, node);

       if (gfpflags & __GFP_WAIT)

              local_irq_disable();

       if (new) {

              c = get_cpu_slab(s, smp_processor_id());

              stat(c, ALLOC_SLAB);

              if (c->page)

                     flush_slab(s, c);

              slab_lock(new);

              __SetPageSlubFrozen(new);

              c->page = new;

              goto load_freelist;

       }

       return NULL;

如果设置了wait位,则先开启中断。然后调用new_slab分配一个页面。如果分配成功,将这个per-cpu变量cpage变成NULL;然后将new赋值给c->page;最后跳转到load_freelist;

load_freelist:

       object = c->page->freelist;

       if (unlikely(!object))

              goto another_slab;

       if (unlikely(SLABDEBUG && PageSlubDebug(c->page)))

              goto debug;

       c->freelist = object[c->offset];

       c->page->inuse = c->page->objects;

       c->page->freelist = NULL;

       c->node = page_to_nid(c->page);

unlock_out:

       slab_unlock(c->page);

       stat(c, ALLOC_SLOWPATH);

       return object;

 

                             new_slab

static struct page *(struct *s, flags, int node)

   1112 {

   1113         struct *;

   1114         void *;

   1115         void *;

   1116         void *p;

   1117

   1118         (flags & );

   1119

   1120         page = (s,

   1121                 flags & ( | ), node);

   1122         if (!page)

   1123                 goto ;

   1124

   1125         inc_slabs_node(s, (page), page->objects);

 

首先调用allocate_slab分配了一个pageinc_slabs_node增加计数。

   1126         page-> = s;

   1127         page->flags |= 1 << ;

   1128         if (s->flags & ( | | |

   1129                          | ))

   1130                 (page);

 

page结构体中的slab指针指向s。设置pageflagPG_SLAG位。

  1132  start = (page);

   1133

   1134         if ((s->flags & ))

   1135                 (start, , << (page));

   1136

   1137          = start;

   1138         (p, s, start, page->objects) {

   1139                 (s, page, );

   1140                 (s, , p);

   1141                  = p;

   1142         }

                     setup_object(s, page, last);

                     set_freepointer(s, last, NULL);

   1147         page-> = 0;

   1148 :

   1149         return page;

 

取出这个page的地址start

然后进入for_each_object

   282 #define (, , , ) \

    283         for ( = (); < () + () * ()->;\

    284                          += ()->)

 

代入参数:

for(p=start;p < start+(page->objects) * s->size;p+=s->size)

{

       *(void **)(last + s->) =p;

       last=p;

}

并且由以前的分析可知,此时s->offset=0;

其中page->objectskmem_cache_open函数的calculate_sizes函数中已经计算,会在

           假设object size8,page的起始地址为0x0000情况如下:

addr     content

0x0000  0x00008

0x0001   data             

……..    data

0x0008  0x0016

……..    data

0x0016  0x0024

………………..

并且设置lastNULL

 

这下再回到__slab_allocload_freelist中:

load_freelist:

       object = c->page->freelist;

       if (unlikely(!object))

              goto another_slab;

       if (unlikely(SLABDEBUG && PageSlubDebug(c->page)))

              goto debug;

       c->freelist = object[c->offset];

       c->page->inuse = c->page->objects;

       c->page->freelist = NULL;

       c->node = page_to_nid(c->page);

unlock_out:

       slab_unlock(c->page);

       stat(c, ALLOC_SLOWPATH);

       return object;

可知:object=c->page->freelist=start=0x0000;

c->freelist=object[0]=0x0008;即它指向了下一个object;

然后返回这个0x0000作为分配到的object

   一开始我认为这要返回的话,一旦往里面写数据,肯定会把这个object里的地址给覆盖的。所以猜想,这个信息会在free时重建。

                   kfree

void kfree(const void *x)

{

       struct page *page;

       void *object = (void *)x;

       trace_kfree(_RET_IP_, x);

       if (unlikely(ZERO_OR_NULL_PTR(x)))

              return;

       page = virt_to_head_page(x);

       if (unlikely(!PageSlab(page))) {

              BUG_ON(!PageCompound(page));

              put_page(page);

              return;

       }

       slab_free(page->slab, page, object, _RET_IP_);

}

 

static __always_inline void slab_free(struct kmem_cache *s,

                     struct page *page, void *x, unsigned long addr)

{

       void **object = (void *)x;

       struct kmem_cache_cpu *c;

       unsigned long flags;

       local_irq_save(flags);

       c = get_cpu_slab(s, smp_processor_id());

       debug_check_no_locks_freed(object, c->objsize);

       if (!(s->flags & SLAB_DEBUG_OBJECTS))

              debug_check_no_obj_freed(object, c->objsize);

       if (likely(page == c->page && c->node >= 0)) {

              object[c->offset] = c->freelist;

              c->freelist = object;

              stat(c, FREE_FASTPATH);

       } else

              __slab_free(s, page, x, addr, c->offset);

       local_irq_restore(flags);

}

 

addr     content

0x0000  data

0x0001   data             

……..    data

0x0008  data

……..    data

c->freelist  0x0016  0x0024

………………..

 

假如已经分配了两个objc->freelist=0x0016;

此时要free 第一个。

addr     content

c->freelist  0x0000  0x00016

0x0001   data             

……..    data

0x0008  data

……..    data

0x0016  0x0024

………………..

c->freelist=0x0000;

如果还得再free第二个object;

object=(void *)x=0x0008;

object[0]=0x0000;

c->freelist=0x0008;

addr     content

0x0000  0x00016

0x0001   data             

……..    data

c->freelist    0x0008  0x0000

……..    data

0x0016  0x0024

………………..

 


阅读(2894) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-08-23 18:32:49

假设object size为8,page的起始地址为0x0000情况如下: addr content 0x0000 0x00008 0x0001 data …….. data 0x0008 0x0016 …….. data 0x0016 0x0024 ==================== 以上错了。 在s->offset = 0的时候,slub是直接使用objsize的前8个字节存储下一个free pointer, 也就是说占用object的前8个字节。 因此object的size的8的时候,格式是这样的(x86_64): addr content 0x0000 0000 0000 0000 : 0x08 0x0000 0000 0000 0001 : 0x00 …….. 0x00 0x0000 0000 0000 0008 : 0x16