我们的分析顺序是初始化、创建cache、申请对象、申请对象时会涉及创建slab、释放对象、释放对象时会涉及释放slab、销毁cache。
前面已经介绍到了创建slab,本节分析一下释放对象,有两个入口kfree和kmem_cache_free,由于两者实现的本质相同只是对外体现为了两个API,
这里放在一起梳理一下,下面分析一下代码。
-
/**
-
* kfree - free previously allocated memory
-
* @objp: pointer returned by kmalloc.
-
*
-
* If @objp is NULL, no operation is performed.
-
*
-
* Don't free memory not originally allocated by kmalloc()
-
* or you will run into trouble.
-
*/
-
void kfree(const void *objp)
-
{
-
struct kmem_cache *c;
-
unsigned long flags;
-
-
trace_kfree(_RET_IP_, objp);
-
-
/* 检查需要释放的对象地址是否有效 */
-
if (unlikely(ZERO_OR_NULL_PTR(objp)))
-
return;
-
local_irq_save(flags);
-
kfree_debugcheck(objp);
-
/* 首先根据对象地址获取其所在的page,然后page的lru->prev指向了slab,page的lru->next指向了cache */
-
/* 在前文中介绍申请slab的cache_grow函数中调用的slab_map_pages完成了Page和cache/slab的映射关系 */
-
c = virt_to_cache(objp);
-
debug_check_no_locks_freed(objp, obj_size(c));
-
debug_check_no_obj_freed(objp, obj_size(c));
-
/* 找到了释放对象所属的cache,下面函数进行释放 */
-
__cache_free(c, (void *)objp);
-
local_irq_restore(flags);
-
}
-
/*
-
* Release an obj back to its cache. If the obj has a constructed state, it must
-
* be in this state _before_ it is released. Called with disabled ints.
-
*/
-
static inline void __cache_free(struct kmem_cache *cachep, void *objp)
-
{
-
/* 获取当前cache中的local cache,即cachep->array[smp_processor_id()] */
-
struct array_cache *ac = cpu_cache_get(cachep);
-
-
check_irq_off();
-
kmemleak_free_recursive(objp, cachep->flags);
-
objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
-
-
kmemcheck_slab_free(cachep, objp, obj_size(cachep));
-
-
/*
-
* Skip calling cache_free_alien() when the platform is not numa.
-
* This will avoid cache misses that happen while accessing slabp (which
-
* is per page memory reference) to get nodeid. Instead use a global
-
* variable to skip the call, which is mostly likely to be present in
-
* the cache.
-
*/
-
/* 判断当前是否为NUMA架构,对于NUMA架构需要判断释放的对象所在的内核节点与当前CPU所属的内存节点是否相同 */
-
/* 如果相同,则执行后续的释放操作;如果不同在cache_free_alien函数中完成释放,并在下面return */
-
/* 假设当前为UMA架构,先继续往下分析,cache_free_alien留在后面分析 */
-
if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))
-
return;
-
/* 判断当前local cache中的可用对象数量是否超过了上限 */
-
if (likely(ac->avail < ac->limit)) {
-
/* 未超上限 */
-
/* 增加释放的命中计数 */
-
STATS_INC_FREEHIT(cachep);
-
/* 将释放对象直接保存在local cache中,申请时取[--ac->avail]释放时是[ac->avail++],可以看出每次申请的都是最近释放的对象 */
-
ac->entry[ac->avail++] = objp;
-
return;
-
} else {
-
/* 已超出上限 */
-
/* 增加释放未命中的计数 */
-
STATS_INC_FREEMISS(cachep);
-
/* 由于local cache中对象数量太多,需要释放batch_count个对象出去,后面我们会分析cache_flusharray函数 */
-
cache_flusharray(cachep, ac);
-
/* local cache中已经释放了一批,已有新空间记录最近释放的对象 */
-
ac->entry[ac->avail++] = objp;
-
}
-
}
-
static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
-
{
-
int batchcount;
-
struct kmem_list3 *l3;
-
/* 根据当前cpu获取所在的内存节点ID */
-
int node = numa_node_id();
-
/* 每次申请和释放,需要批量处理的对象个数 */
-
batchcount = ac->batchcount;
-
#if DEBUG
-
BUG_ON(!batchcount || batchcount > ac->avail);
-
#endif
-
check_irq_off();
-
/* 获取当前cache的slab 三链,kmem_list3结构 */
-
l3 = cachep->nodelists[node];
-
spin_lock(&l3->list_lock);
-
/* 如果有shared 的local cache */
-
if (l3->shared) {
-
struct array_cache *shared_array = l3->shared;
-
/* 计算当前shared local cache还能容纳多少slab对象,limit是上限,avail是当前拥有的空闲数量 */
-
int max = shared_array->limit - shared_array->avail;
-
if (max) {
-
/* 不能超过shared local cache的容纳上限 */
-
if (batchcount > max)
-
batchcount = max;
-
/* Entry数组中记录的都是可用对象的地址,直接批量拷贝过去即可 */
-
memcpy(&(shared_array->entry[shared_array->avail]),
-
ac->entry, sizeof(void *) * batchcount);
-
/* 更新shared local cache当前的可用对象数量 */
-
shared_array->avail += batchcount;
-
goto free_done;
-
}
-
}
-
/* 没有shared cache或者shared cache已经放满了,无法融入更多的对象,此时需要释放到slab三链中 */
-
free_block(cachep, ac->entry, batchcount, node);
-
free_done:
-
#if STATS
-
{
-
int i = 0;
-
struct list_head *p;
-
-
p = l3->slabs_free.next;
-
while (p != &(l3->slabs_free)) {
-
struct slab *slabp;
-
-
slabp = list_entry(p, struct slab, list);
-
BUG_ON(slabp->inuse);
-
-
i++;
-
p = p->next;
-
}
-
STATS_SET_FREEABLE(cachep, i);
-
}
-
#endif
-
spin_unlock(&l3->list_lock);
-
/* 至此,已批量释放batchcount个对象,调整avail的值 */
-
ac->avail -= batchcount;
-
/* 由于先释放的是旧的对象,即数组前面的对象,所以需要后面的对象整体前移 */
-
memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
-
}
-
/*
-
* Caller needs to acquire correct kmem_list's list_lock
-
*/
-
/* 没有shared cache,或者shared cache已经满载的情况下,会调用该函数进行回收
-
cachep: 需要回收对象的cache
-
objpp: local cache中记录slab对象的数组首地址
-
nr_objects: 需要释放的slab对象的数量
-
node: 当前cpu所在的NUMA节点,也是当前slab对象所属的NUMA节点*/
-
static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
-
int node)
-
{
-
int i;
-
struct kmem_list3 *l3;
-
/* 遍历需要释放的对象 */
-
for (i = 0; i < nr_objects; i++) {
-
/* 根据索引,获取需要释放的对象 */
-
void *objp = objpp[i];
-
struct slab *slabp;
-
/* 根据slan对象获取其所属的slab,首先根据对象地址获取其所在page,根据page->lru.prev可以获取slab */
-
slabp = virt_to_slab(objp);
-
/* 根据NUMA节点获取slab三链 */
-
l3 = cachep->nodelists[node];
-
/* 将slab从原有链表上摘除,现在在那个链表上(free/paritcal/full三链之一) */
-
list_del(&slabp->list);
-
check_spinlock_acquired_node(cachep, node);
-
check_slabp(cachep, slabp);
-
/* 将对象释放到slab中 */
-
/* 更新slab->inuse计数,更新slab->free指向当前释放的对象索引,更新slab->bufctl(slabp)[当前对象索引]指向上一节点索引 */
-
slab_put_obj(cachep, slabp, objp, node);
-
/* 减少当前cache中活跃对象的数量 */
-
STATS_DEC_ACTIVE(cachep);
-
/* 增加slab三链中可用空闲对象的计数 */
-
l3->free_objects++;
-
check_slabp(cachep, slabp);
-
-
/* fixup slab chains */
-
/* 判断当前slab是否已经没有正在使用的对象 */
-
if (slabp->inuse == 0) {
-
/* 当前slab三链中空闲对象数量已经超过了上限 */
-
if (l3->free_objects > l3->free_limit) {
-
/* 此时需要销毁一个slab,而每个slab中含有cache->num个对象,所以先把free_objects更新了,然后销毁slab */
-
l3->free_objects -= cachep->num;
-
/* No need to drop any previously held
-
* lock here, even if we have a off-slab slab
-
* descriptor it is guaranteed to come from
-
* a different cache, refer to comments before
-
* alloc_slabmgmt.
-
*/
-
/* 销毁一个slab,见后文单独篇幅分析 */
-
slab_destroy(cachep, slabp);
-
} else {
-
/* 由于slab的所有对象已空闲,将slab挂在free链表上 */
-
list_add(&slabp->list, &l3->slabs_free);
-
}
-
} else {
-
/* Unconditionally move a slab to the end of the
-
* partial list on free - maximum time for the
-
* other objects to be freed, too.
-
*/
-
/* 将slab挂在到partical链表上 */
-
list_add_tail(&slabp->list, &l3->slabs_partial);
-
}
-
}
-
}
阅读(2914) | 评论(0) | 转发(1) |