歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux內存管理之slab機制(分配對象)

Linux內存管理之slab機制(分配對象)

日期:2017/2/28 15:59:54   编辑:Linux教程
Linux內核從slab中分配內存空間上層函數由kmalloc()或kmem_cache_alloc()函數實現。

kmalloc()->__kmalloc()->__do_kmalloc()

  1. /**
  2. * __do_kmalloc - allocate memory
  3. * @size: how many bytes of memory are required.
  4. * @flags: the type of memory to allocate (see kmalloc).
  5. * @caller: function caller for debug tracking of the caller
  6. */
  7. static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
  8. void *caller)
  9. {
  10. struct kmem_cache *cachep;
  11. void *ret;
  12. /* If you want to save a few bytes .text space: replace
  13. * __ with kmem_.
  14. * Then kmalloc uses the uninlined functions instead of the inline
  15. * functions.
  16. */
  17. /*查找指定大小的通用cache,關於sizes[]數組,在前面
  18. 的初始化中就已經分析過了*/
  19. cachep = __find_general_cachep(size, flags);
  20. if (unlikely(ZERO_OR_NULL_PTR(cachep)))
  21. return cachep;
  22. /*實際的分配工作*/
  23. ret = __cache_alloc(cachep, flags, caller);
  24. trace_kmalloc((unsigned long) caller, ret,
  25. size, cachep->buffer_size, flags);
  26. return ret;
  27. }

實際的分配工作:__do_cache_alloc()->__cache_alloc()->____cache_alloc()

  1. /*從指定cache中分配對象*/
  2. static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
  3. {
  4. void *objp;
  5. struct array_cache *ac;
  6. check_irq_off();
  7. /* 獲得本CPU的local cache */
  8. ac = cpu_cache_get(cachep);
  9. /* 如果local cache中有可用的空閒對象 */
  10. if (likely(ac->avail)) {
  11. /* 更新local cache命中計數 */
  12. STATS_INC_ALLOCHIT(cachep);
  13. /* touched置1表示最近使用了local cache,這會影響填充
  14. local cache時的數目,最近使用的填充較多的對象 */
  15. ac->touched = 1;
  16. /* 從local cache的entry數組中提取最後面的空閒對象 */
  17. objp = ac->entry[--ac->avail];
  18. } else {
  19. /* local cache中沒有空閒對象,更新未命中計數 */
  20. STATS_INC_ALLOCMISS(cachep);
  21. /* 從slab三鏈中提取空閒對象填充到local cache中 */
  22. objp = cache_alloc_refill(cachep, flags);
  23. #if 0/*這裡是我新加的,這裡可能是這個版本的一個bug,在最新的內核裡面這塊已經加上了*/
  24. /*
  25. * the 'ac' may be updated by cache_alloc_refill(),
  26. * and kmemleak_erase() requires its correct value.
  27. */
  28. /* cache_alloc_refill的cache_grow打開了中斷,local cache指針可能發生了變化,需要重新獲得 */
  29. ac = cpu_cache_get(cachep);
  30. #endif
  31. }
  32. /*
  33. * To avoid a false negative, if an object that is in one of the
  34. * per-CPU caches is leaked, we need to make sure kmemleak doesn't
  35. * treat the array pointers as a reference to the object.
  36. */ /* 分配出去的對象,其entry指針指向空 */
  37. kmemleak_erase(&ac->entry[ac->avail]);
  38. return objp;
  39. }

該函數的執行流程:

1,從本地CPU cache中查找是否有空閒的對象;

2,如果本地CPU cache 中沒有空閒對象,從slab三鏈中提取空閒對象,此操作由函數cache_alloc_refill()實現

1)如果存在共享本地cache,那麼將共享本地cache中的對象批量復制到本地cache。

2)如果沒有shared local cache,或是其中沒有空閒的對象,從slab鏈表中分配,其中,從slab中分配時,先查看部分空余鏈表,然後再查看空余鏈表。將slab鏈表中的數據先放到本地CPU cache中。

3) 如果本地CPU cache中任然沒有數據,那麼只有重新創建一個slab,然後再試。

  1. /*從slab三鏈中提取一部分空閒對象填充到local cache中*/
  2. static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
  3. {
  4. int batchcount;
  5. struct kmem_list3 *l3;
  6. struct array_cache *ac;
  7. int node;
  8. retry:
  9. check_irq_off();
  10. /* 獲得本內存節點,UMA只有一個節點 */
  11. node = numa_node_id();
  12. /* 獲得本CPU的local cache */
  13. ac = cpu_cache_get(cachep);
  14. /* 批量填充的數目,local cache是按批填充的 */
  15. batchcount = ac->batchcount;
  16. if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
  17. /*
  18. * If there was little recent activity on this cache, then
  19. * perform only a partial refill. Otherwise we could generate
  20. * refill bouncing.
  21. */
  22. /* 最近未使用過此local cache,沒有必要添加過多的對象
  23. ,添加的數目為默認的限定值 */
  24. batchcount = BATCHREFILL_LIMIT;
  25. }
  26. /* 獲得本內存節點、本cache的slab三鏈 */
  27. l3 = cachep->nodelists[node];
  28. BUG_ON(ac->avail > 0 || !l3);
  29. spin_lock(&l3->list_lock);
  30. /* See if we can refill from the shared array */
  31. /* shared local cache用於多核系統中,為所有cpu共享
  32. ,如果slab cache包含一個這樣的結構
  33. ,那麼首先從shared local cache中批量搬運空閒對象到local cache中
  34. 。通過shared local cache使填充工作變得簡單。*/
  35. if (l3->shared && transfer_objects(ac, l3->shared, batchcount))
  36. goto alloc_done;
  37. /* 如果沒有shared local cache,或是其中沒有空閒的對象
  38. ,從slab鏈表中分配 */
  39. while (batchcount > 0) {
  40. struct list_head *entry;
  41. struct slab *slabp;
  42. /* Get slab alloc is to come from. */
  43. /* 先從部分滿slab鏈表中分配 */
  44. entry = l3->slabs_partial.next;
  45. /* next指向頭節點本身,說明部分滿slab鏈表為空 */
  46. if (entry == &l3->slabs_partial) {
  47. /* 表示剛剛訪問了slab空鏈表 */
  48. l3->free_touched = 1;
  49. /* 檢查空slab鏈表 */
  50. entry = l3->slabs_free.next;
  51. /* 空slab鏈表也為空,必須增加slab了 */
  52. if (entry == &l3->slabs_free)
  53. goto must_grow;
  54. }
  55. /* 獲得鏈表節點所在的slab */
  56. slabp = list_entry(entry, struct slab, list);
  57. /*調試用*/
  58. check_slabp(cachep, slabp);
  59. check_spinlock_acquired(cachep);
  60. /*
  61. * The slab was either on partial or free list so
  62. * there must be at least one object available for
  63. * allocation.
  64. */
  65. BUG_ON(slabp->inuse >= cachep->num);
  66. while (slabp->inuse < cachep->num && batchcount--) {
  67. /* 更新調試用的計數器 */
  68. STATS_INC_ALLOCED(cachep);
  69. STATS_INC_ACTIVE(cachep);
  70. STATS_SET_HIGH(cachep);
  71. /* 從slab中提取一個空閒對象,將其虛擬地址插入到local cache中 */
  72. ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
  73. node);
  74. }
  75. check_slabp(cachep, slabp);
  76. /* move slabp to correct slabp list: */
  77. /* 從原鏈表中刪除此slab節點,list表示此
  78. slab位於哪個鏈表(滿、部分滿、空)中 */
  79. list_del(&slabp->list);
  80. /*因為從中刪除了一個slab,需要從新檢查*/
  81. if (slabp->free == BUFCTL_END)
  82. /* 此slab中已經沒有空閒對象,添加到“full”slab鏈表中 */
  83. list_add(&slabp->list, &l3->slabs_full);
  84. else
  85. /* 還有空閒對象,添加到“partial”slab鏈表中 */
  86. list_add(&slabp->list, &l3->slabs_partial);
  87. }
  88. must_grow:
  89. /* 前面從slab鏈表中添加avail個空閒對象到local cache中
  90. ,更新slab鏈表的空閒對象數 */
  91. l3->free_objects -= ac->avail;
  92. alloc_done:
  93. spin_unlock(&l3->list_lock);
  94. /* local cache中仍沒有可用的空閒對象,說明slab
  95. 三鏈中也沒有空閒對象,需要創建新的空slab了 */
  96. if (unlikely(!ac->avail)) {
  97. int x;
  98. /* 創建一個空slab */
  99. x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
  100. /* cache_grow can reenable interrupts, then ac could change. */
  101. /* 上面的操作使能了中斷,此期間local cache指針可能發生了變化,需要重新獲得 */
  102. ac = cpu_cache_get(cachep);
  103. /* 無法新增空slab,local cache中也沒有空閒對象,表明系統已經無法分配新的空閒對象了 */
  104. if (!x && ac->avail == 0) /* no objects in sight? abort */
  105. return NULL;
  106. /* 走到這有兩種可能,第一種是無論新增空slab成功或失敗,只要avail不為0
  107. ,表明是其他進程重填了local cache,本進程就不需要重填了
  108. ,不執行retry流程。第二種是avail為0,並且新增空slab成功
  109. ,則進入retry流程,利用新分配的空slab填充local cache */
  110. if (!ac->avail) /* objects refilled by interrupt? */
  111. goto retry;
  112. }
  113. /* 重填了local cache,設置近期訪問標志 */
  114. ac->touched = 1;
  115. /* 返回local cache中最後一個空閒對象的虛擬地址 */
  116. return ac->entry[--ac->avail];
  117. }
Copyright © Linux教程網 All Rights Reserved