Chinaunix首页 | 论坛 | 博客
  • 博客访问: 174110
  • 博文数量: 50
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 123
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-01 16:03
文章分类

全部博文(50)

文章存档

2016年(3)

2015年(5)

2014年(35)

2013年(7)

我的朋友

分类: LINUX

2014-04-04 11:55:06

上文创建好slab后,本文主要讲解如何从里面分配对象。
 
kmem_cache_alloc

slab系统中获得一个空闲的对象。调用关系为:kmem_cache_alloc->__cache_alloc-> __do_cache_alloc-> ____cache_alloc

直接看____cache_alloc函数。

static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)

{

       void *objp;           

       struct array_cache *ac;

 

       check_irq_off();

/* 获得本CPUlocal cache */

       ac = cpu_cache_get(cachep);

       /* 如果local cache中有可用的空闲对象 */

       if (likely(ac->avail)) {

                      /* 更新local cache命中计数 */

                      STATS_INC_ALLOCHIT(cachep);

/* touched1表示最近使用了local cache,这会影响填充local cache时的数目,最近使用的填充较多的对象 */

                      ac->touched = 1;

                      /* local cacheentry数组中提取最后面的空闲对象 */

                      objp = ac->entry[--ac->avail];

       } else {

                      /* local cache中没有空闲对象,更新未命中计数 */

                      STATS_INC_ALLOCMISS(cachep);

                      /* slab三链中提取空闲对象填充到local cache */

                      objp = cache_alloc_refill(cachep, flags);

                      /*

                       * the 'ac' may be updated by cache_alloc_refill(),

                       * and kmemleak_erase() requires its correct value.

                       */

/* cache_alloc_refillcache_grow打开了中断,local cache指针可能发生了变化,需要重新获得 */

                      ac = cpu_cache_get(cachep);

       }

       /*

        * To avoid a false negative, if an object that is in one of the

        * per-CPU caches is leaked, we need to make sure kmemleak doesn't

        * treat the array pointers as a reference to the object.

        */

       /* 分配出去的对象,其entry指针指向空 */

       if (objp)

                      kmemleak_erase(&ac->entry[ac->avail]);

       return objp;

}

 

cache_alloc_refill

slab三链中提取一部分空闲对象填充到local cache中。

static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)

{

       int batchcount;

       struct kmem_list3 *l3;

       struct array_cache *ac;

       int node;

 

retry:

       check_irq_off();

       /* 获得本内存节点,UMA只有一个节点 */

       node = numa_mem_id();

       /* 获得本CPUlocal cache */

       ac = cpu_cache_get(cachep);

       /* 批量填充的数目,local cache是按批填充的 */

       batchcount = ac->batchcount;

       if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {

                      /*

                       * If there was little recent activity on this cache, then

                       * perform only a partial refill.  Otherwise we could generate

                       * refill bouncing.

                       */

/* 最近未使用过此local cache,没有必要添加过多的对象,添加的数目为默认的限定值 */

                      batchcount = BATCHREFILL_LIMIT;

       }

       /* 获得本内存节点、本cacheslab三链 */

       l3 = cachep->nodelists[node];

 

       BUG_ON(ac->avail > 0 || !l3);

       spin_lock(&l3->list_lock);

 

       /* See if we can refill from the shared array */

/* shared local cache用于多核系统中,为所有cpu共享,如果slab cache包含一个这样的结构,那么首先从shared local cache中批量搬运空闲对象到local cache中。通过shared local cache使填充工作变得简单。*/

       if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) {

                      l3->shared->touched = 1;

                      goto alloc_done;

       }

       /* 如果没有shared local cache,或是其中没有空闲的对象,从slab链表中分配 */

       while (batchcount > 0) {

                      struct list_head *entry;

                      struct slab *slabp;

                      /* Get slab alloc is to come from. */

                      /* 先从部分满slab链表中分配 */

                      entry = l3->slabs_partial.next;

                      /* next指向头节点本身,说明部分满slab链表为空 */

                      if (entry == &l3->slabs_partial) {

/* 表示刚刚访问了slab空链表 */

                                    l3->free_touched = 1;

                                    /* 检查空slab链表 */

                                    entry = l3->slabs_free.next;

                                    /* slab链表也为空,必须增加slab */

                                    if (entry == &l3->slabs_free)

                                                  goto must_grow;

                      }

                      /* 获得链表节点所在的slab */

                      slabp = list_entry(entry, struct slab, list);

                      check_slabp(cachep, slabp);

                      check_spinlock_acquired(cachep);

 

                      /*

                       * The slab was either on partial or free list so

                       * there must be at least one object available for

                       * allocation.

                       */

                      BUG_ON(slabp->inuse >= cachep->num);

 

                      while (slabp->inuse < cachep->num && batchcount--) {

                                    /* 更新调试用的计数器 */

                                    STATS_INC_ALLOCED(cachep);

                                    STATS_INC_ACTIVE(cachep);

                                    STATS_SET_HIGH(cachep);

                                    /* slab中提取一个空闲对象,将其虚拟地址插入到local cache */

                                    ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,

                                                                                                 node);

                      }

                      check_slabp(cachep, slabp);

 

                      /* move slabp to correct slabp list: */

/* 从原链表中删除此slab节点,list表示此slab位于哪个链表(满、部分满、空)中 */

                      list_del(&slabp->list);

                      if (slabp->free == BUFCTL_END)

                                    /* slab中已经没有空闲对象,添加到“fullslab链表中 */

                                    list_add(&slabp->list, &l3->slabs_full);

                      else

                                    /* 还有空闲对象,添加到“partialslab链表中 */

                                    list_add(&slabp->list, &l3->slabs_partial);

       }

 

must_grow:

/* 前面从slab链表中添加avail个空闲对象到local cache中,更新slab链表的空闲对象数 */

       l3->free_objects -= ac->avail;

alloc_done:

       spin_unlock(&l3->list_lock);

/* local cache中仍没有可用的空闲对象,说明slab三链中也没有空闲对象,需要创建新的空slab */

       if (unlikely(!ac->avail)) {

                      int x;

                      /* 创建一个空slab */

                      x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);

 

                      /* cache_grow can reenable interrupts, then ac could change. */

/* 上面的操作使能了中断,此期间local cache指针可能发生了变化,需要重新获得 */

                      ac = cpu_cache_get(cachep);

/* 无法新增空slablocal cache中也没有空闲对象,表明系统已经无法分配新的空闲对象了 */

                      if (!x && ac->avail == 0)      /* no objects in sight? abort */

                                    return NULL;

/* 走到这有两种可能,第一种是无论新增空slab成功或失败,只要avail不为0,表明是其他进程重填了local cache,本进程就不需要重填了,不执行retry流程。第二种是avail0,并且新增空slab成功,则进入retry流程,利用新分配的空slab填充local cache */

                      if (!ac->avail)                       /* objects refilled by interrupt? */

                                    goto retry;

       }

       /* 重填了local cache,设置近期访问标志 */

       ac->touched = 1;

       /* 返回local cache中最后一个空闲对象的虚拟地址 */

       return ac->entry[--ac->avail];

}

 

slab_get_obj

slab中提取一个空闲对象。

static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,

                                                  int nodeid)

{

       /* 获得一个空闲的对象,free是本slab中第一个空闲对象的索引 */

       void *objp = index_to_obj(cachep, slabp, slabp->free);

       kmem_bufctl_t next;

       /* 更新在用对象计数 */

       slabp->inuse++;

       /* 获得第一个空闲对象的索引 */

       next = slab_bufctl(slabp)[slabp->free];

#if DEBUG

       slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;

       WARN_ON(slabp->nodeid != nodeid);

#endif

       /* free指向下一个空闲的对象 */

       slabp->free = next;

 

       return objp;

}

 

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