歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux Slob分配器(二)--分配對象

Linux Slob分配器(二)--分配對象

日期:2017/3/1 10:18:26   编辑:Linux編程

上節介紹了Slob分配器的相關概念和思想,這節來看Slob分配器是如何分配對象的。kmem_cache_alloc_node()函數用來分配一個專用緩存的對象:

相關閱讀:
Linux Slob分配器(一)--概述 http://www.linuxidc.com/Linux/2012-07/64107.htm
Linux Slob分配器(三)--釋放對象 http://www.linuxidc.com/Linux/2012-07/64109.htm

  1. void *kmem_cache_alloc_node(struct kmem_cache *c, gfp_t flags, int node)
  2. {
  3. void *b;
  4. if (c->size < PAGE_SIZE) {//對象小於PAGE_SIZE,由Slob分配器進行分配
  5. b = slob_alloc(c->size, flags, c->align, node);
  6. trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,
  7. SLOB_UNITS(c->size) * SLOB_UNIT,
  8. flags, node);
  9. } else {//否則通過伙伴系統分配
  10. b = slob_new_pages(flags, get_order(c->size), node);
  11. trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,
  12. PAGE_SIZE << get_order(c->size),
  13. flags, node);
  14. }
  15. if (c->ctor)//如果定義了構造函數則調用構造函數
  16. c->ctor(b);
  17. kmemleak_alloc_recursive(b, c->size, 1, c->flags, flags);
  18. return b;
  19. }

由於slob為PAGE_SIZE大小,因此首先要判斷要求分配的對象的大小是否在這個范圍內,如果是,則通過Slob分配器來分配,否則的話通過伙伴系統分配。

來看Slob分配對象的具體過程

  1. static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
  2. {
  3. struct slob_page *sp;
  4. struct list_head *prev;
  5. struct list_head *slob_list;
  6. slob_t *b = NULL;
  7. unsigned long flags;
  8. /*根據分配對象的大小選擇從哪個鏈表的slob中進行分配*/
  9. if (size < SLOB_BREAK1)
  10. slob_list = &free_slob_small;
  11. else if (size < SLOB_BREAK2)
  12. slob_list = &free_slob_medium;
  13. else
  14. slob_list = &free_slob_large;
  15. spin_lock_irqsave(&slob_lock, flags);
  16. /* Iterate through each partially free page, try to find room */
  17. list_for_each_entry(sp, slob_list, list) {//遍歷slob鏈表
  18. #ifdef CONFIG_NUMA
  19. /*
  20. * If there's a node specification, search for a partial
  21. * page with a matching node id in the freelist.
  22. */
  23. if (node != -1 && page_to_nid(&sp->page) != node)//節點不匹配
  24. continue;
  25. #endif
  26. /* Enough room on this page? */
  27. if (sp->units < SLOB_UNITS(size))//slob中的空間不夠
  28. continue;
  29. /* Attempt to alloc */
  30. prev = sp->list.prev;
  31. b = slob_page_alloc(sp, size, align);//分配對象
  32. if (!b)
  33. continue;
  34. /* Improve fragment distribution and reduce our average
  35. * search time by starting our next search here. (see
  36. * Knuth vol 1, sec 2.5, pg 449) */
  37. /*這裡將slob_list鏈表頭移動到prev->next前面,以便下次遍歷時能夠從prev->next開始遍歷*/
  38. if (prev != slob_list->prev &&
  39. slob_list->next != prev->next)
  40. list_move_tail(slob_list, prev->next);
  41. break;
  42. }
  43. spin_unlock_irqrestore(&slob_lock, flags);
  44. /* Not enough space: must allocate a new page */
  45. if (!b) {//沒有分配到對象,也就是說slob_list中沒有可以滿足分配要求的slob了
  46. b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);//創建新的slob
  47. if (!b)
  48. return NULL;
  49. sp = slob_page(b);//獲取slob的地址
  50. set_slob_page(sp);
  51. spin_lock_irqsave(&slob_lock, flags);
  52. sp->units = SLOB_UNITS(PAGE_SIZE);//計算單元數
  53. sp->free = b; //設置首個空閒塊的地址
  54. INIT_LIST_HEAD(&sp->list);
  55. set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));
  56. set_slob_page_free(sp, slob_list); //將sp鏈入slob_list
  57. b = slob_page_alloc(sp, size, align);//從新的slob中分配塊
  58. BUG_ON(!b);
  59. spin_unlock_irqrestore(&slob_lock, flags);
  60. }
  61. if (unlikely((gfp & __GFP_ZERO) && b))
  62. memset(b, 0, size);
  63. return b;
  64. }
  • 首先要根據對象的大小來決定從哪個全局鏈表中尋找slob進行分配
  • 遍歷選取的鏈表,找到一個空間足夠滿足分配要求的slob
  • 從選取的slob中分配對象塊(slob_page_alloc())
  • 如果遍歷完整個鏈表都沒能分配到對象,則創建一個新的slob(slob_new_page()),然後設置slob的屬性,再進行分配,可以看到一個新的slob中只有一個塊,並且下一個空閒對象的指針指向了下一頁的起始處,也就是頁對齊的

來看分配的細節操作slab_page_alloc()

  1. static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)
  2. {
  3. slob_t *prev, *cur, *aligned = NULL;
  4. int delta = 0, units = SLOB_UNITS(size);
  5. for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) {
  6. slobidx_t avail = slob_units(cur);//計算獲取的空閒塊的容量
  7. /*如果設置了對齊值則先將塊進行對齊*/
  8. if (align) {
  9. aligned = (slob_t *)ALIGN((unsigned long)cur, align);
  10. delta = aligned - cur;//計算對齊後的對象增加了多少字節的內存
  11. }
  12. /*空閒塊內存不小於要求分配的 units+對齊增量*/
  13. if (avail >= units + delta) { /* room enough? */
  14. slob_t *next;
  15. /*確實進行了對齊操作*/
  16. if (delta) { /* need to fragment head to align? */
  17. next = slob_next(cur);//獲取下一個空閒塊
  18. /*這裡將原本的一個塊分裂成了兩個塊*/
  19. set_slob(aligned, avail - delta, next);//設置空閒對象偏移aligned-->next
  20. set_slob(cur, delta, aligned);//設置空閒對象偏移cur--->aligned
  21. /*調整prev指針以及cur指針,都向後移動一個塊*/
  22. prev = cur;
  23. cur = aligned;
  24. avail = slob_units(cur);//重新獲取單元數
  25. }
  26. next = slob_next(cur);//獲取下一個空閒塊
  27. if (avail == units) { /* 空閒塊的大小和要求的大小完全相符 */
  28. if (prev)//存在先驅塊,則將先驅塊的指針指向next塊
  29. set_slob(prev, slob_units(prev), next);
  30. else//不存在先驅塊說明為第一個塊,則將free直接指向next
  31. sp->free = next;
  32. } else { /* 大小不相符,則要將塊分裂*/
  33. if (prev)
  34. set_slob(prev, slob_units(prev), cur + units);
  35. else
  36. sp->free = cur + units;
  37. set_slob(cur + units, avail - units, next);
  38. }
  39. sp->units -= units;//減少slob的單元數
  40. if (!sp->units)//單元數為0表明slob沒有空閒單元,則從鏈表中刪除
  41. clear_slob_page_free(sp);
  42. return cur;
  43. }
  44. if (slob_last(cur))
  45. return NULL;
  46. }
  47. }
Copyright © Linux教程網 All Rights Reserved