Linux內存管理之slab機制(釋放對象)
Linux內存管理之slab機制(釋放對象)
Linux內核中將對象釋放到slab中上層所用函數為kfree()或kmem_cache_free()。兩個函數都會調用__cache_free()函數。
代碼執行流程:
1,當本地CPU cache中空閑對象數小於規定上限時,只需將對象放入本地CPU cache中;
2,當local cache中對象過多(大於等於規定上限),需要釋放一批對象到slab三鏈中。由函數cache_flusharray()實現。
1)如果三鏈中存在共享本地cache,那麼首先選擇釋放到共享本地cache中,能釋放多少是多少;
2)如果沒有shared local cache,釋放對象到slab三鏈中,實現函數為free_block()。對於free_block()函數,當三鏈中的空閑對象數過多時,銷毀此cache。不然,添加此slab到空閑鏈表。因為在分配的時候我們看到將slab結構從cache鏈表中脫離了,在這裡,根據page描述符的lru找到slab並將它添加到三鏈的空閑鏈表中。
主實現
view plaincopy to clipboard01./*
02. * Release an obj back to its cache. If the obj has a constructed state, it must
03. * be in this state _before_ it is released. Called with disabled ints.
04. */
05.static inline void __cache_free(struct kmem_cache *cachep, void *objp)
06.{
07. /* 獲得本CPU的local cache */
08. struct array_cache *ac = cpu_cache_get(cachep);
09.
10. check_irq_off();
11. kmemleak_free_recursive(objp, cachep->flags);
12. objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
13.
14. kmemcheck_slab_free(cachep, objp, obj_size(cachep));
15.
16. /*
17. * Skip calling cache_free_alien() when the platform is not numa.
18. * This will avoid cache misses that happen while accessing slabp (which
19. * is per page memory reference) to get nodeid. Instead use a global
20. * variable to skip the call, which is mostly likely to be present in
21. * the cache.
22. *//* NUMA相關 */
23. if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))
24. return;
25.
26. if (likely(ac->avail < ac->limit)) {
27. /* local cache中的空閑對象數小於上限時
28. ,只需將對象釋放回entry數組中 */
29. STATS_INC_FREEHIT(cachep);
30. ac->entry = objp;
31. return;
32. } else {
33. /* 大於等於上限時, */
34. STATS_INC_FREEMISS(cachep);
35. /* local cache中對象過多,需要釋放一批對象到slab三鏈中。*/
36. cache_flusharray(cachep, ac);
37. ac->entry = objp;
38. }
39.} 釋放對象到三鏈中
view plaincopy to clipboard01./*local cache中對象過多,需要釋放一批對象到slab三鏈中。*/
02.static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
03.{
04. int batchcount;
05. struct kmem_list3 *l3;
06. int node = numa_node_id();
07. /* 每次釋放多少個對象 */
08. batchcount = ac->batchcount;
09.#if DEBUG
10. BUG_ON(!batchcount || batchcount > ac->avail);
11.#endif
12. check_irq_off();
13. /* 獲得此cache的slab三鏈 */
14. l3 = cachep->nodelists;
15. spin_lock(&l3->list_lock);
16. if (l3->shared) {
17. /* 如果存在shared local cache,將對象釋放到其中 */
18. struct array_cache *shared_array = l3->shared;
19. /* 計算shared local cache中還有多少空位 */
20. int max = shared_array->limit - shared_array->avail;
21. if (max) {
22. /* 空位數小於要釋放的對象數時,釋放數等於空位數 */
23. if (batchcount > max)
24. batchcount = max;
25. /* 釋放local cache前面的幾個對象到shared local cache中
26. ,前面的是最早不用的 */
27. memcpy(&(shared_array->entry),
28. ac->entry, sizeof(void *) * batchcount);
29. /* 增加shared local cache可用對象數 */
30. shared_array->avail += batchcount;
31. goto free_done;
32. }
33. }
34. /* 無shared local cache,釋放對象到slab三鏈中 */
35. free_block(cachep, ac->entry, batchcount, node);
36.free_done:
37.#if STATS
38. {
39. int i = 0;
40. struct list_head *p;
41.
42. p = l3->slabs_free.next;
43. while (p != &(l3->slabs_free)) {
44. struct slab *slabp;
45.
46. slabp = list_entry(p, struct slab, list);
47. BUG_ON(slabp->inuse);
48.
49. i++;
50. p = p->next;
51. }
52. STATS_SET_FREEABLE(cachep, i);
53. }
54.#endif
55. spin_unlock(&l3->list_lock);
56. /* 減少local cache可用對象數*/
57. ac->avail -= batchcount;
58. /* local cache前面有batchcount個空位,將後面的對象依次前移batchcount位 */
59. memmove(ac->entry, &(ac->entry), sizeof(void *)*ac->avail);
60.} 無shared local cache,釋放對象到slab三鏈中
view plaincopy to clipboard01./*
02. * Caller needs to acquire correct kmem_list's list_lock
03. */
04. /*釋放一定數目的對象*/
05.static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
06. int node)
07.{
08. int i;
09. struct kmem_list3 *l3;
10. /* 逐一釋放對象到slab三鏈中 */
11. for (i = 0; i < nr_objects; i++) {
12. void *objp = objpp;
13. struct slab *slabp;
14. /* 通過虛擬地址得到page,再通過page得到slab */
15. slabp = virt_to_slab(objp);
16. /* 獲得slab三鏈 */
17. l3 = cachep->nodelists;
18. /* 先將對象所在的slab從鏈表中摘除 */
19. list_del(&slabp->list);
20. check_spinlock_acquired_node(cachep, node);
21. check_slabp(cachep, slabp);
22. /* 將對象釋放到其slab中 */
23. slab_put_obj(cachep, slabp, objp, node);
24. STATS_DEC_ACTIVE(cachep);
25. /* 空閑對象數加一 */
26. l3->free_objects++;
27. check_slabp(cachep, slabp);
28.
29. /* fixup slab chains */
30. if (slabp->inuse == 0) {
31. /* 如果slab中均為空閑對象 */
32. if (l3->free_objects > l3->free_limit) {
33. /* 如果slab三鏈中空閑對象數超過上限
34. ,直接回收整個slab到內存
35. ,空閑對象數減去每個slab中對象數 */
36. l3->free_objects -= cachep->num;
37. /* No need to drop any previously held
38. * lock here, even if we have a off-slab slab
39. * descriptor it is guaranteed to come from
40. * a different cache, refer to comments before
41. * alloc_slabmgmt.
42. *//* 銷毀struct slab對象 */
43. slab_destroy(cachep, slabp);
44. } else {
45. /* 將此slab添加到空slab鏈表中 */
46. list_add(&slabp->list, &l3->slabs_free);
47. }
48. } else {
49. /* Unconditionally move a slab to the end of the
50. * partial list on free - maximum time for the
51. * other objects to be freed, too.
52. *//*將此slab添加到部分滿slab鏈表中*/
53. list_add_tail(&slabp->list, &l3->slabs_partial);
54. }
55. }
56.} 將對象釋放到其slab中
view plaincopy to clipboard01.static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
02. void *objp, int nodeid)
03.{ /* 獲得對象在kmem_bufctl_t數組中的索引 */
04. unsigned int objnr = obj_to_index(cachep, slabp, objp);
05.
06.#if DEBUG
07. /* Verify that the slab belongs to the intended node */
08. WARN_ON(slabp->nodeid != nodeid);
09.
10. if (slab_bufctl(slabp) + 1 <= SLAB_LIMIT + 1) {
11. printk(KERN_ERR "slab: double free detected in cache "
12. "'%s', objp %p\n", cachep->name, objp);
13. BUG();
14. }
15.#endif
16. /*這兩步相當於靜態鏈表的插入操作*/
17. /* 指向slab中原來的第一個空閑對象 */
18. slab_bufctl(slabp) = slabp->free;
19. /* 釋放的對象作為第一個空閑對象 */
20. slabp->free = objnr;
21. /* 已分配對象數減一 */
22. slabp->inuse--;
23.} 輔助函數
view plaincopy to clipboard01./* 通過虛擬地址得到page,再通過page得到slab */
02.static inline struct slab *virt_to_slab(const void *obj)
03.{
04. struct page *page = virt_to_head_page(obj);
05. return page_get_slab(page);
06.} view plaincopy to clipboard01.static inline struct slab *page_get_slab(struct page *page)
02.{
03. BUG_ON(!PageSlab(page));
04. return (struct slab *)page->lru.prev;
05.}
可見,用page->lru.prev得到slab,和創建slab時相呼應。
《解決方案》
謝謝分享