Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379670
  • 博文数量: 87
  • 博客积分: 983
  • 博客等级: 准尉
  • 技术积分: 685
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-25 07:20
文章分类

全部博文(87)

文章存档

2016年(1)

2015年(3)

2014年(55)

2013年(13)

2012年(15)

分类: LINUX

2014-07-11 15:58:35

 

    本节介绍如何从slub分配器中分配一个空闲对象。

 

kmem_cache_alloc

 

    通过slub分配器获得一个空闲对象。直接调用slab_alloc函数。

 

slab_alloc

    首先尝试快速路径分配,如果失败再走慢速路径分配。

    参数:

1)        scache指针。

2)        gfpflags:分配标志。

3)        nodeNUMA,从指定内存节点分配

4)        addr:初始调用函数地址

 

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;

       /* CONFIG_SLUB_DEBUG相关 */

       if (slab_pre_alloc_hook(s, gfpflags))

              return NULL;

 

       local_irq_save(flags);

       /* 获得本cpulocal slab__this_cpu_ptr只是新版本的cpu_slab[NR_CPUS] */

       c = __this_cpu_ptr(s->cpu_slab);

       /* 获得local slab中的第一个空闲对象 */

       object = c->freelist;

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

              /* local slab中无空闲对象或local slab与指定的内存节点不匹配,走慢速路径分配 */

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

 

       else {

              /* freelist指向下一个空闲对象。get_freepointer返回下一个空闲对象的地址 */

              c->freelist = get_freepointer(s, object);

              /* 快速路径local slab中分配成功 */

              stat(s, ALLOC_FASTPATH);

       }

       local_irq_restore(flags);

       /* 是否需要将对象清0 */

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

              memset(object, 0, s->objsize);

/* CONFIG_SLUB_DEBUG相关 */

       slab_post_alloc_hook(s, gfpflags, object);

 

       return object;

}

 

 __slab_alloc

 

    无法从local slab中分配空闲对象,走慢速路径分配。

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;

/* 还没有local slab,从部分满slab链中提取一个slab作为local slabPage指针保存local slab的首页 */

       if (!c->page)

              goto new_slab;

 

       slab_lock(c->page);

       /* local slab与指定内存节点不匹配,去激活local slab */

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

              goto another_slab;

       /* local slab重填统计 */

       stat(s, ALLOC_REFILL);

/* local slab freelist中的空闲对象填充到cpu freelist */

load_freelist:

/* 检查local slabfreelist指针 */

       object = c->page->freelist;

       /* local slab没有空闲对象,去激活local slab */

       if (unlikely(!object))

              goto another_slab;

       if (kmem_cache_debug(s))

              goto debug;

/* local slab freelist中的对象全部填充到cpu freelist中。这个“填充”动作实际上只是一个指针接管 */

       c->freelist = get_freepointer(s, object);

/* local slab的所有空闲对象都分配给了cpu freelist(而不是一般意义上的分配给进程)*/

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

/* slab freelist无空闲对象了,全部分给了cpu freelist */

       c->page->freelist = NULL;

       /* local slab所在的内存节点 */

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

unlock_out:

       slab_unlock(c->page);

       /* 更新慢速路径分配统计 */

       stat(s, ALLOC_SLOWPATH);

       /* 返回空闲对象 */

       return object;

 

another_slab:

       /* 去激活旧的local slab */

       deactivate_slab(s, c);

 

new_slab:

/* 提取一个部分满的slab。如果没有指定node,那么可能来自于本内存节点,也可能来自于其他节点。如果指定了node,那么只可能来自于本内存节点。*/

       new = get_partial(s, gfpflags, node);

       if (new) {

              /* local slab指向新的slab */

              c->page = new;

              stat(s, ALLOC_FROM_PARTIAL);

              /* 重试freelist填充流程 */

              goto load_freelist;

       }

       /* 走到这说明没有部分满的slab了,需要创建一个新slab */

       gfpflags &= gfp_allowed_mask;

       if (gfpflags & __GFP_WAIT)

              local_irq_enable();

       /* 创建一个新slab */

       new = new_slab(s, gfpflags, node);

 

       if (gfpflags & __GFP_WAIT)

              local_irq_disable();

 

       if (new) {

              /* 获得local slab所在的struct kmem_cache_cpu指针 */

              c = __this_cpu_ptr(s->cpu_slab);

              /* 更新统计 */

              stat(s, ALLOC_SLAB);

              /* 清除旧的local slab */

              if (c->page)

                     flush_slab(s, c);

              slab_lock(new);

              /* 冻结新的slab,表示它已经成为某个cpulocal slab */

              __SetPageSlubFrozen(new);

              /* 指向新的local slab */

              c->page = new;

/* 重试freelist填充流程 */

              goto load_freelist;

       }

       /* 走到这,说明已经没有空闲内存,分配失败 */

       if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit())

              slab_out_of_memory(s, gfpflags, node);

       return NULL;

debug:

       if (!alloc_debug_processing(s, c->page, object, addr))

              goto another_slab;

 

       c->page->inuse++;

       c->page->freelist = get_freepointer(s, object);

       c->node = NUMA_NO_NODE;

       goto unlock_out;

}

 

get_freepointer

 

static inline void *get_freepointer(struct kmem_cache *s, void *object)

{

/* 每个对象后面会放一个对象指针,指向下一个空闲对象。此处返回下一个空闲对象的指针。虽然cache中可能被不同的对象引用,对象大小可能有些许不同,但是offset是固定的 */

       return *(void **)(object + s->offset);

}

 

deactivate_slab

    去激活local slab。如果slab双链中的一个slab成为某cpulocal slab,我们称之为激活,反之,如果将某个local slab重新放回到slab双链中,我们称之为去激活。

static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)

       __releases(bitlock)

{

       /* 获得local slab的首页 */

       struct page *page = c->page;

       int tail = 1;

/* slabfreelist不为空。通常一个local slabfreelist是空的,其对象已经全部分配给了cpufeelist,参见__slab_alloc。不为空,说明有其他cpu释放了属于此cpu local slab的对象,参见__slab_free */

       if (page->freelist)

              stat(s, DEACTIVATE_REMOTE_FREES);

       /*

        * Merge cpu freelist into slab freelist. Typically we get here

        * because both freelists are empty. So this is unlikely

        * to occur.

        */

/* 首先将cpu freelist中的空闲对象归还给slab freelist。一个slab成为local slab时,其freelist中的对象均已分配cpu freelist。通常分配对象走到这时,cpu freelist为空,仅当内存节点不匹配时,cpu freelist可能非空。*/

       while (unlikely(c->freelist)) {

              void **object;

              /* 指定了插入到部分满slab链表时的位置,最近使用的放到链表头 */

              tail = 0;   /* Hot objects. Put the slab first */

 

              /* Retrieve object from cpu_freelist */

              object = c->freelist;

              c->freelist = get_freepointer(s, c->freelist);

 

              /* And put onto the regular freelist */

              set_freepointer(s, object, page->freelist);

              page->freelist = object;

              page->inuse--;

       }

       /* local slab指针置为空 */

       c->page = NULL;

       /* 解冻local slab */

       unfreeze_slab(s, page, tail);

}

 

unfreeze_slab

    激活一个slab成为local slab后,会被冻结。去激活slab时,需要解冻。

static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail)

       __releases(bitlock)

{

       struct kmem_cache_node *n = get_node(s, page_to_nid(page));

       /* 清除冻结标记,表示不是local slab */

       __ClearPageSlubFrozen(page);

       if (page->inuse) {

              /* slab中某些对象仍在使用 */

              if (page->freelist) {

                     /* slab还有空闲对象,加入部分满slab链,tail0时,添加到链表头 */

                     add_partial(n, page, tail);

                     stat(s, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD);

              } else {

                     /* slab中没有空闲对象,加入满slab */

                     stat(s, DEACTIVATE_FULL);

                     if (kmem_cache_debug(s) && (s->flags & SLAB_STORE_USER))

                            /* 加入满slab */

                            add_full(n, page);

              }

              slab_unlock(page);

       } else {

              /* slab中所有对象都是空闲的 */

              stat(s, DEACTIVATE_EMPTY);

              if (n->nr_partial < s->min_partial) {

                     /* 如果部分满slab个数小于最小值 */

                     /*

                      * Adding an empty slab to the partial slabs in order

                      * to avoid page allocator overhead. This slab needs

                      * to come after the other slabs with objects in

                      * so that the others get filled first. That way the

                      * size of the partial list stays small.

                      *

                      * kmem_cache_shrink can reclaim any empty slabs from

                      * the partial list.

                      */

/* 将空slab加入到部分满slab链的后面。为了避免部分满slab链过多,内核会通过kmem_cache_shrink对部分满slab链按照inuse的大小进行排序(最大的在前),并回收空slab*/

                     add_partial(n, page, 1);

                     slab_unlock(page);

              } else {

                     /* 部分满slab个数满足最小值的要求,无需再维护这个slab */

                     slab_unlock(page);

                     stat(s, FREE_SLAB);

                     /* 销毁这个slab */

                     discard_slab(s, page);

              }

       }

}

 

discard_slab

    销毁一个slab

 

static void discard_slab(struct kmem_cache *s, struct page *page)

{

       /* 更新统计 */

       dec_slabs_node(s, page_to_nid(page), page->objects);

       /* 销毁slab */

       free_slab(s, page);

}

 

get_partial

    提取一个部分满的slab

static struct page *get_partial(struct kmem_cache *s, gfp_t flags, int node)

{

       struct page *page;

       /* 计算扫描节点,未指定NUMA节点,扫描本节点,否则扫描指定节点 */

       int searchnode = (node == NUMA_NO_NODE) ? numa_node_id() : node;

       /* 从扫描节点提取一个部分满slab */

       page = get_partial_node(get_node(s, searchnode));

       if (page || node != -1)

              return page;

       /* 从指定节点提取失败,扫描任意节点 */

       return get_any_partial(s, flags);

}

 

get_partial_node

    从某内存节点提取一个部分满slab

static struct page *get_partial_node(struct kmem_cache_node *n)

{

       struct page *page;

 

       /*

        * Racy check. If we mistakenly see no partial slabs then we

        * just allocate an empty slab. If we mistakenly try to get a

        * partial slab and there is none available then get_partials()

        * will return NULL.

        */

       if (!n || !n->nr_partial)

              return NULL;

 

       spin_lock(&n->list_lock);

       /* 遍历部分满slab */

       list_for_each_entry(page, &n->partial, lru)

              /* 一个slab成为local slab后,需要被冻结 */

              if (lock_and_freeze_slab(n, page))

                     goto out;

       page = NULL;

out:

       spin_unlock(&n->list_lock);

       return page;

}

 

lock_and_freeze_slab

 

static inline int lock_and_freeze_slab(struct kmem_cache_node *n,

                                                 struct page *page)

{

       if (slab_trylock(page)) {

              /* 从部分满slab链上摘除 */

              __remove_partial(n, page);

              /* 冻结某个slab,表示此slab已经变成了某个cpulocal slab */

              __SetPageSlubFrozen(page);

              return 1;

       }

       return 0;

}

 

flush_slab

    清除local slab

static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)

{

       /* 更新统计 */

       stat(s, CPUSLAB_FLUSH);

       slab_lock(c->page);

       /* 去激活local slab */

       deactivate_slab(s, c);

}

 

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