Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3666014
  • 博文数量: 146
  • 博客积分: 3918
  • 博客等级: 少校
  • 技术积分: 8581
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 13:52
个人简介

个人微薄: weibo.com/manuscola

文章分类

全部博文(146)

文章存档

2016年(3)

2015年(2)

2014年(5)

2013年(42)

2012年(31)

2011年(58)

2010年(5)

分类: LINUX

2011-05-29 16:20:11

slab系统中获得一个空闲的对象,调用流程大概是:
kmem_cache_alloc-------->__cache_alloc---------->__do_cache_alloc--->____cache_alloc

ac->avail这个字段表示Local Cache可用的obj个数。
如果存在可用的obj,可以直接将当前obj的指针放回,同时,ac->avail自减。

如果没有,可用的obj,那么需要填充Cache,即  slab三链中提取空闲对象填充到local cache 。
这个功能是由函数cache_alloc_refill 完成。
static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
void *objp;
struct array_cache *ac;

check_irq_off();

ac = cpu_cache_get(cachep);
if (likely(ac->avail)) {
STATS_INC_ALLOCHIT(cachep);
ac->touched = 1;
objp = ac->entry[--ac->avail];
} else {
STATS_INC_ALLOCMISS(cachep);
objp = cache_alloc_refill(cachep, flags);
}
return objp;
}

1  cache_alloc_refill 函数。


首先选择共享Cache里面的obj,如果共享的cache中有可用的obj,那么可以,不用从
三个链表中取。
if (l3->shared && transfer_objects(ac, l3->shared, batchcount))
goto alloc_done;

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

B Share Cache 失败,只能从三链中取obj
首选slabs_partial,如果 slab_partial为空,无法取出,则求助于l3->slabs_free,
如果 l3->slabs_free再次为空,那么只能cache_grow。 分配新的slab。

当然我们首先考虑 三链中存在可用的obj,那么将三链中的obj 填入local cache。
ac->entry[ac->avail++] = slab_get_obj(cachep, slabp, node);
同时注意调整slab位于的链表位置。
如果slab中的obj被取光了 ,则把slab链入 l3->slabs_full,不然就链入l3->slabs_partial

C 如果经过从三链搬运obj,Local Cache中还是没有obj,换句话说,就是三链中的slab中也没有存粮了。
那就没办法了,只能用狠招了,新建一个slab。
 即判断条件为 if (unlikely(!ac->avail)) //从Share Cache 或三链中搬运了半天,都没有可用的obj

这个工作是由 cache_grow干成的。

2 cache_grow 函数。
首先是计算颜色值, l3->colour_next是下一个颜色值,在上一篇博文中提到,cachep->colour是颜色的最大种类,颜色种类个数并不能无限制,上文提到是 (left_over / 缓存线的大小)。也就是说,并不是想映射到那个 Cache Line都可以。如果下一个颜色值大于等于最大颜色值 cachep->colour,颜色值为0. 从这个地方我们也可以看到 两个同种类的obj 可能映射到同一条Cache line,造成不断换入换出缓存。
offset = l3->colour_next;
l3->colour_next++;
if (l3->colour_next >= cachep->colour)
l3->colour_next = 0;
                   offset *= cachep->colour_off;
接下来是为obj 和slab头分配空间。
A obj是通过kmem_getpages 函数来分配的。
这个函数的实现过程是 如下:
1 调用伙伴内存系统的 alloc_pages_node分配需要的页面,页面数保存在cachep->gfporder。
2 给分配的每一个页面打上Slab标志位
3返回第一页的位置 。return page_address(page);

objp = kmem_getpages(cachep, local_flags, nodeid);
B 分配slab头 是通过 alloc_slabmgmt实现。
我们知道,slab头可能位于slab上,也可能在slab外。这取决于cachep的标志位 CFLGS_OFF_SLAB是否置1
1)如果slab头在slab外。
slabp = kmem_cache_alloc_node(cachep->slabp_cache, local_flags & ~GFP_THISNODE, nodeid);
if (!slabp)
return NULL;
还记得上篇博文提到的 死循环吗?如果obj的个数太多,导致slab头大于obj的大小,会导致死循环,因为,
slab头也是通过kmem_cache_alloc_node 分配,分配slab头时候,可能slab头的slab头大于slab头的大小,
.....从而导致死循环的发生。好在,上一篇提到,通过slab_break_gfp_order  =1 ,杜绝了这种可能性。
2)slab头在slab上、
slabp = objp + colour_off; 
colour_off += cachep->slab_size;

   colour_off是上文提到offset。
  offset = l3->colour_next;
    offset *= cachep->colour_off;
slab头的位置,并不是在页面的开始位置,而是偏移了Cache Line的整数倍,倍数为颜色值。这是为了映射到不同的Cache Line.
然后更新  colour_off = 偏移量 +slab头的大小。
slabp->inuse = 0;
slabp->colouroff = colour_off;
slabp->s_mem = objp + colour_off; //obj数据区的起始地址。不论slab头位于slab上还是slab外。
slabp->nodeid = nodeid;
slabp->free = 0; //第一个可用的obj的index。因为是新分配的slab,所以每个obj都可用。

接下来的是 slab_map_pages这个函数 这个函数建立了页面和slab 及cache的联系。

这个函数的短小精悍,言简意赅。把分配的每一个页面,page_set_cache和page_set_slab。
         page->lru.next = (struct list_head *)cache; 
和      page->lru.prev = (struct list_head *)slab;
通过将
static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
  void *addr)
{
int nr_pages;
struct page *page;

page = virt_to_page(addr);

nr_pages = 1;
if (likely(!PageCompound(page)))
nr_pages <<= cache->gfporder;

do {
page_set_cache(page, cache);
page_set_slab(page, slab);
page++;
} while (--nr_pages);
}
 
再接下来是cache_init_objs 这个函数。
这个函数作用是计算每个obj的位置(通过index_to_obj完成)。
slab头有两部分组成 一部分是slab描述符,另一部分是一个数组,数组记录的是下一个可用的obj的index。
slab_bufctl(slabp)[i] = i + 1;

问题是第一个可用的obj的index存到哪里?我们看下slab结构体。
free是第一个可用的obj的index。在alloc_slabmgmt 已经将free置为 0.
struct slab {
struct list_head list;
unsigned long colouroff;
void *s_mem; /* including colour offset */
unsigned int inuse; /* num of objs active in slab */
kmem_bufctl_t free;
unsigned short nodeid;
};

接下来是:
    list_add_tail(&slabp->list, &(l3->slabs_free));  //将新建的slab放入三链中的slabs_free
STATS_INC_GROWN(cachep);
l3->free_objects += cachep->num; //每个slab中有cachep->num个obj。所以可用的obj增加


整个函数介绍完毕了。

感谢palals ,从他的日志中学到了很多东西,而且日志写的并没有palals的好。


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