Linux Slub分配器(五)--釋放對象

 

 水平有限,描述不當之處還請之處,轉載請註明出處http://blog.csdn.net/vanbreaker/article/details/7701910    

       釋放對象和分配對象是一組對稱的操作,同樣分爲兩個路徑:

1.如果待釋放的對象所屬的slab位於本地CPU緩存中,也就是slab處於凍結狀態,則可直接釋放

2.反之,待釋放的對象所屬的slab位於slab鏈表中,也就是slab處於解凍狀態,則要通過慢速路徑進行釋放。

 

函數kmem_cache_free()用來將一個對象釋放至對應的緩存中

void kmem_cache_free(struct kmem_cache *s, void *x)
{
	struct page *page;

	/*找到對象x所屬slab的首頁*/
	page = virt_to_head_page(x);

	/*釋放對象x*/
	slab_free(s, page, x, _RET_IP_);

	trace_kmem_cache_free(_RET_IP_, x);
}


釋放對象前必須先得到對象所屬slab的第一個頁,這樣便於後面判斷slab是否處於本地CPU緩存中。

static __always_inline void slab_free(struct kmem_cache *s,
			struct page *page, void *x, unsigned long addr)
{
	void **object = (void *)x;
	struct kmem_cache_cpu *c;
	unsigned long flags;

	kmemleak_free_recursive(x, s->flags);
	local_irq_save(flags);
	c = get_cpu_slab(s, smp_processor_id());//獲取本地CPU的slab信息結構

	/*相關檢查*/
	kmemcheck_slab_free(s, object, c->objsize);
	debug_check_no_locks_freed(object, c->objsize);
	if (!(s->flags & SLAB_DEBUG_OBJECTS))
		debug_check_no_obj_freed(object, c->objsize);

	/*這裏必須確定待釋放對象所屬的slab的確還處於本地CPU中,因爲在常規分配失敗時
	  本地CPU中的slab可能會被解凍,也就是轉移到slab鏈表中,而解凍時可能還有
	  對象未回收*/
	if (likely(page == c->page && c->node >= 0)) {
		/*釋放的操作和分配的操作是對稱的*/
		object[c->offset] = c->freelist; //設定空閒指針
		c->freelist = object;            //重置freelist
		stat(c, FREE_FASTPATH);
	} else
		__slab_free(s, page, x, addr, c->offset);//慢速釋放路徑

	local_irq_restore(flags);
}


判斷slab是否位於本地CPU緩存中的方式很簡單,就是看本地CPU緩存中的page指針和之前獲取的page指針是否相等,相等的話就表明兩者指向同一個page,也就是說是同一個slab. 判斷node>=0是爲了辨明該非調試狀態,因爲node==-1是用來調試的。常規釋放路徑和分配是對稱的操作,如果看懂了分配,那麼這個自然也就明白了,不贅述了。下面來看慢速釋放路徑

static void __slab_free(struct kmem_cache *s, struct page *page,
			void *x, unsigned long addr, unsigned int offset)
{
	void *prior;
	void **object = (void *)x;
	struct kmem_cache_cpu *c;

	
	c = get_cpu_slab(s, raw_smp_processor_id());
	stat(c, FREE_SLOWPATH);
	slab_lock(page);

	if (unlikely(SLABDEBUG && PageSlubDebug(page)))
		goto debug;

checks_ok:
	/*將對象釋放回原屬的slab*/
	prior = object[offset] = page->freelist;
	page->freelist = object;
	page->inuse--;

	if (unlikely(PageSlubFrozen(page))) {
		stat(c, FREE_FROZEN);
		goto out_unlock;
	}

	/*inuse爲0表示該slab的所有對象都處於空閒狀態*/
	if (unlikely(!page->inuse))
		goto slab_empty;

	/*
	 * Objects left in the slab. If it was not on the partial list before
	 * then add it.
	 */
	 /*prior爲空表明之前page->freelist爲NULL,也就是說slab沒有處於partial slab鏈表中,
	   將該slab添加到partial slab鏈表的尾部*/
	if (unlikely(!prior)) {
		add_partial(get_node(s, page_to_nid(page)), page, 1);
		stat(c, FREE_ADD_PARTIAL);
	}

out_unlock:
	slab_unlock(page);
	return;

slab_empty:
	if (prior) {//prior不爲空表明slab處於partial slab鏈表中
		/*
		 * Slab still on the partial list.
		 */
		remove_partial(s, page);//將slab從partial slab鏈表中刪除
		stat(c, FREE_REMOVE_PARTIAL);
	}
	slab_unlock(page);
	stat(c, FREE_SLAB);
	discard_slab(s, page);//銷燬slab
	return;

debug:
	if (!free_debug_processing(s, page, x, addr))
		goto out_unlock;
	goto checks_ok;
}

首先獲取本地CPU緩存結構,保存在c中,然後將對象釋放回slab,注意這裏用的是page->freelist而不是c->freelist

如果page->inuse爲0,表示slab所有對象都是空閒的,slub沒有free list鏈表,因此選擇直接銷燬該slab,將內存返回給夥伴系統。如果prior爲空,那也就表示slab處於full slab鏈表而不是partial slab鏈表,由於現在獲得了一個空閒對象,因此將slab添加到partial slab中,至於從full slab中刪除slab的操作,是在free_debug_processing()中完成的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章