在前文分析kmalloc时,首先从local cache中获取,没有空闲对象时再从shared local cache中获取,再从slab的三个链表中获取。
当链表中也没有空闲对象时,则需要调用cache_grow函数来创建slab了。实际上刚开始创建cache时里面是没有slab对象的,真正使用时才进行slab的创建。
下面分析一下cache_grow的实现。
-
/*
-
* Grow (by 1) the number of slabs within a cache. This is called by
-
* kmem_cache_alloc() when there are no active objs left in a cache.
-
*/
-
/* cachep: 需要扩容的cache; flags: 是否可以阻塞等; nodeie:对应的NUMA节点ID;
-
objp: 页面虚拟地址,为NULL代表尚未申请到页面;不为空代表已经申请,可以直接创建slab */
-
static int cache_grow(struct kmem_cache *cachep,
-
gfp_t flags, int nodeid, void *objp)
-
{
-
struct slab *slabp;
-
size_t offset;
-
gfp_t local_flags;
-
struct kmem_list3 *l3;
-
-
/*
-
* Be lazy and only check for valid flags here, keeping it out of the
-
* critical path in kmem_cache_alloc().
-
*/
-
BUG_ON(flags & GFP_SLAB_BUG_MASK);
-
local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
-
-
/* Take the l3 list lock to change the colour_next on this node */
-
check_irq_off();
-
/* 根据NUMA节点ID获取slab三链 */
-
l3 = cachep->nodelists[nodeid];
-
spin_lock(&l3->list_lock);
-
-
/* Get colour for the slab, and cal the next value. */
-
/* 获取slab的着色偏移 */
-
offset = l3->colour_next;
-
/* 更新着色偏移,使不同slab的着色偏移不同 */
-
l3->colour_next++;
-
/* 不能超过着色区的大小,否则重新循环 */
-
if (l3->colour_next >= cachep->colour)
-
l3->colour_next = 0;
-
spin_unlock(&l3->list_lock);
-
/* 根据着色区个数*着色偏移,计算着色区的大小 */
-
offset *= cachep->colour_off;
-
/* 是否允许阻塞,否则开启硬中断 */
-
if (local_flags & __GFP_WAIT)
-
local_irq_enable();
-
-
/*
-
* The test for missing atomic flag is performed here, rather than
-
* the more obvious place, simply to reduce the critical path length
-
* in kmem_cache_alloc(). If a caller is seriously mis-behaving they
-
* will eventually be caught here (where it matters).
-
*/
-
kmem_flagcheck(cachep, flags);
-
-
/*
-
* Get mem for the objs. Attempt to allocate a physical page from
-
* 'nodeid'.
-
*/
-
/* 是否已经申请页面,如尚未申请,则先申请 */
-
/* 分配1<gfporder个页面,objp为slab首页面地址 */
-
if (!objp)
-
objp = kmem_getpages(cachep, local_flags, nodeid);
-
if (!objp)
-
goto failed;
-
-
/* Get slab management. */
-
/* 分配slab管理对象 */
-
slabp = alloc_slabmgmt(cachep, objp, offset,
-
local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
-
if (!slabp)
-
goto opps1;
-
/* 设置page到cache slab的映射 */
-
slab_map_pages(cachep, slabp, objp);
-
/* 初始化slab中的对象 */
-
cache_init_objs(cachep, slabp);
-
-
if (local_flags & __GFP_WAIT)
-
local_irq_disable();
-
check_irq_off();
-
spin_lock(&l3->list_lock);
-
-
/* Make slab active. */
-
/* 将申请到的slab挂入free链表上 */
-
list_add_tail(&slabp->list, &(l3->slabs_free));
-
/* 增加计数 */
-
STATS_INC_GROWN(cachep);
-
/* 更新空闲对象数量 */
-
l3->free_objects += cachep->num;
-
spin_unlock(&l3->list_lock);
-
return 1;
-
opps1:
-
kmem_freepages(cachep, objp);
-
failed:
-
if (local_flags & __GFP_WAIT)
-
local_irq_disable();
-
return 0;
-
}
-
/*
-
* Get the memory for a slab management obj.
-
* For a slab cache when the slab descriptor is off-slab, slab descriptors
-
* always come from malloc_sizes caches. The slab descriptor cannot
-
* come from the same cache which is getting created because,
-
* when we are searching for an appropriate cache for these
-
* descriptors in kmem_cache_create, we search through the malloc_sizes array.
-
* If we are creating a malloc_sizes cache here it would not be visible to
-
* kmem_find_general_cachep till the initialization is complete.
-
* Hence we cannot have slabp_cache same as the original cache.
-
*/
-
/* 申请slab管理对象 */
-
static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
-
int colour_off, gfp_t local_flags,
-
int nodeid)
-
{
-
struct slab *slabp;
-
/* 是否为外置slab */
-
if (OFF_SLAB(cachep)) {
-
/* Slab management obj is off-slab. */
-
/* 外置slab,从slabp_cache中申请一个对象,slabp_cache是在初始化阶段被设置上的 */
-
slabp = kmem_cache_alloc_node(cachep->slabp_cache,
-
local_flags, nodeid);
-
/*
-
* If the first object in the slab is leaked (it's allocated
-
* but no one has a reference to it), we want to make sure
-
* kmemleak does not treat the ->s_mem pointer as a reference
-
* to the object. Otherwise we will not report the leak.
-
*/
-
kmemleak_scan_area(slabp, offsetof(struct slab, list),
-
sizeof(struct list_head), local_flags);
-
if (!slabp)
-
return NULL;
-
} else {
-
/* 内置slab */
-
/* objp为slab的首页面的虚拟地址,根据着色偏移,计算slabp管理对象的地址 */
-
slabp = objp + colour_off;
-
/* slab_size是管理对象大小,包含了slab和kmem_bufctl,此处计算第一个slab对象的偏移量 */
-
colour_off += cachep->slab_size;
-
}
-
/* 新申请的slab对象,已用对象数为0 */
-
slabp->inuse = 0;
-
/* 第1个对象的页面偏移 */
-
/* 内置slab,则为着色区偏移+slab_size;外置slab,则为着色区偏移 */
-
slabp->colouroff = colour_off;
-
/* 第一个对象的虚拟地址 */
-
slabp->s_mem = objp + colour_off;
-
/* slab所属于的NUMA节点ID */
-
slabp->nodeid = nodeid;
-
/* free标示可用对象的数组下标,刚申请完,所以数组中所有成员可用,从0开始 */
-
slabp->free = 0;
-
return slabp;
-
}
继续盗用两张图(http://blog.csdn.net/bullbat/article/details/7190105),展示一下slab的组织形式
继续分析一下slab_map_pages,该函数会建立slab/cache与page之间的映射,可以通过page的lru链表找到page所属的slab和cache。
-
/*
-
* Map pages beginning at addr to the given cache and slab. This is required
-
* for the slab allocator to be able to lookup the cache and slab of a
-
* virtual address for kfree, ksize, kmem_ptr_validate, and slab debugging.
-
*/
-
/* cache/slab地址,以及addr 虚拟页面的首地址 */
-
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用于slab时,page的lru->next指向page所在cache, lru->prev指向page所在的slab */
-
/* 当page空闲时,lru负责将page串联在一起 */
-
page_set_cache(page, cache);
-
page_set_slab(page, slab);
-
page++;
-
} while (--nr_pages);
-
}
如下,来自(http://blog.csdn.net/bullbat/article/details/7190105):
-
/
-
static void cache_init_objs(struct kmem_cache *cachep,
-
struct slab *slabp)
-
{
-
int i;
-
-
/* index_to_obj: slab->s_mem + cache->buffer_size * idx */
-
/* s_mem为第一个对象的起始地址,buffer_size为对象的大小,idx为索引 */
-
for (i = 0; i < cachep->num; i++) {
-
/* 遍历slab中的所有对象,先获取对象的地址 */
-
void *objp = index_to_obj(cachep, slabp, i);
-
#if DEBUG
-
/* need to poison the objs? */
-
if (cachep->flags & SLAB_POISON)
-
poison_obj(cachep, objp, POISON_FREE);
-
if (cachep->flags & SLAB_STORE_USER)
-
*dbg_userword(cachep, objp) = NULL;
-
-
if (cachep->flags & SLAB_RED_ZONE) {
-
*dbg_redzone1(cachep, objp) = RED_INACTIVE;
-
*dbg_redzone2(cachep, objp) = RED_INACTIVE;
-
}
-
/*
-
* Constructors are not allowed to allocate memory from the same
-
* cache which they are a constructor for. Otherwise, deadlock.
-
* They must also be threaded.
-
*/
-
if (cachep->ctor && !(cachep->flags & SLAB_POISON))
-
cachep->ctor(objp + obj_offset(cachep));
-
-
if (cachep->flags & SLAB_RED_ZONE) {
-
if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)
-
slab_error(cachep, "constructor overwrote the"
-
" end of an object");
-
if (*dbg_redzone1(cachep, objp) != RED_INACTIVE)
-
slab_error(cachep, "constructor overwrote the"
-
" start of an object");
-
}
-
if ((cachep->buffer_size % PAGE_SIZE) == 0 &&
-
OFF_SLAB(cachep) && cachep->flags & SLAB_POISON)
-
kernel_map_pages(virt_to_page(objp),
-
cachep->buffer_size / PAGE_SIZE, 0);
-
#else
-
/* 如果cache指定了构造函数,则使用构造函数进行初始化 */
-
if (cachep->ctor)
-
cachep->ctor(objp);
-
#endif
-
/* slab_bufctl: (kmem_bufctl_t *) (slabp + 1) */
-
/* kmem_bufctl数组紧接着struct slab存放,其中每个成员的值都代表了下一个可用对象的索引 */
-
/* 建立静态数组的索引,其中slab->free指向了当前可用的空闲对象,然后slab_bufctl(slabp)[slab->free]指向了下一个可用对象的索引 */
-
slab_bufctl(slabp)[i] = i + 1;
-
}
-
/* 最后一个元素指向BUCTL_END,代表无法继续获取空闲对象 */
-
slab_bufctl(slabp)[i - 1] = BUFCTL_END;
-
}
阅读(3193) | 评论(0) | 转发(1) |