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


上节介绍了Slob分配器的相关概念和思想,这节来看Slob分配器是如何分配对象的。kmem_cache_alloc_node()函数用来分配一个专用缓存的对象:

相关阅读:
Linux Slob分配器(一)--概述
Linux Slob分配器(三)--释放对象

  1. void *kmem_cache_alloc_node(struct kmem_cache *c, gfp_t flags, int node)  
  2. {  
  3.     void *b;  
  4.   
  5.     if (c->size < PAGE_SIZE) {//对象小于PAGE_SIZE,由Slob分配器进行分配   
  6.         b = slob_alloc(c->size, flags, c->align, node);  
  7.         trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,  
  8.                         SLOB_UNITS(c->size) * SLOB_UNIT,  
  9.                         flags, node);  
  10.     } else {//否则通过伙伴系统分配   
  11.         b = slob_new_pages(flags, get_order(c->size), node);  
  12.         trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,  
  13.                         PAGE_SIZE << get_order(c->size),  
  14.                         flags, node);  
  15.     }  
  16.   
  17.     if (c->ctor)//如果定义了构造函数则调用构造函数   
  18.         c->ctor(b);  
  19.   
  20.     kmemleak_alloc_recursive(b, c->size, 1, c->flags, flags);  
  21.     return b;  
  22. }

由于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.   
  9.     /*根据分配对象的大小选择从哪个链表的slob中进行分配*/  
  10.     if (size < SLOB_BREAK1)  
  11.         slob_list = &free_slob_small;  
  12.     else if (size < SLOB_BREAK2)  
  13.         slob_list = &free_slob_medium;  
  14.     else  
  15.         slob_list = &free_slob_large;  
  16.   
  17.     spin_lock_irqsave(&slob_lock, flags);  
  18.     /* Iterate through each partially free page, try to find room */  
  19.     list_for_each_entry(sp, slob_list, list) {//遍历slob链表   
  20. #ifdef CONFIG_NUMA   
  21.         /* 
  22.          * If there's a node specification, search for a partial 
  23.          * page with a matching node id in the freelist. 
  24.          */  
  25.         if (node != -1 && page_to_nid(&sp->page) != node)//节点不匹配   
  26.             continue;  
  27. #endif   
  28.         /* Enough room on this page? */  
  29.         if (sp->units < SLOB_UNITS(size))//slob中的空间不够   
  30.             continue;  
  31.   
  32.         /* Attempt to alloc */  
  33.         prev = sp->list.prev;  
  34.         b = slob_page_alloc(sp, size, align);//分配对象   
  35.         if (!b)  
  36.             continue;  
  37.   
  38.         /* Improve fragment distribution and reduce our average 
  39.          * search time by starting our next search here. (see 
  40.          * Knuth vol 1, sec 2.5, pg 449) */  
  41.          /*这里将slob_list链表头移动到prev->next前面,以便下次遍历时能够从prev->next开始遍历*/  
  42.         if (prev != slob_list->prev &&  
  43.                 slob_list->next != prev->next)  
  44.             list_move_tail(slob_list, prev->next);  
  45.         break;  
  46.     }  
  47.     spin_unlock_irqrestore(&slob_lock, flags);  
  48.   
  49.     /* Not enough space: must allocate a new page */  
  50.     if (!b) {//没有分配到对象,也就是说slob_list中没有可以满足分配要求的slob了   
  51.         b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);//创建新的slob   
  52.         if (!b)  
  53.             return NULL;  
  54.         sp = slob_page(b);//获取slob的地址   
  55.         set_slob_page(sp);  
  56.   
  57.         spin_lock_irqsave(&slob_lock, flags);  
  58.         sp->units = SLOB_UNITS(PAGE_SIZE);//计算单元数   
  59.         sp->free = b;    //设置首个空闲块的地址   
  60.         INIT_LIST_HEAD(&sp->list);  
  61.         set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));  
  62.         set_slob_page_free(sp, slob_list);    //将sp链入slob_list   
  63.         b = slob_page_alloc(sp, size, align);//从新的slob中分配块   
  64.         BUG_ON(!b);  
  65.         spin_unlock_irqrestore(&slob_lock, flags);  
  66.     }  
  67.     if (unlikely((gfp & __GFP_ZERO) && b))  
  68.         memset(b, 0, size);  
  69.     return b;  
  70. }  
  • 首先要根据对象的大小来决定从哪个全局链表中寻找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.   
  6.     for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) {  
  7.         slobidx_t avail = slob_units(cur);//计算获取的空闲块的容量   
  8.   
  9.         /*如果设置了对齐值则先将块进行对齐*/  
  10.         if (align) {  
  11.             aligned = (slob_t *)ALIGN((unsigned long)cur, align);  
  12.             delta = aligned - cur;//计算对齐后的对象增加了多少字节的内存   
  13.         }  
  14.   
  15.         /*空闲块内存不小于要求分配的 units+对齐增量*/  
  16.         if (avail >= units + delta) { /* room enough? */  
  17.             slob_t *next;  
  18.   
  19.             /*确实进行了对齐操作*/  
  20.             if (delta) { /* need to fragment head to align? */  
  21.                 next = slob_next(cur);//获取下一个空闲块   
  22.   
  23.                 /*这里将原本的一个块分裂成了两个块*/  
  24.                 set_slob(aligned, avail - delta, next);//设置空闲对象偏移aligned-->next   
  25.                 set_slob(cur, delta, aligned);//设置空闲对象偏移cur--->aligned   
  26.   
  27.                 /*调整prev指针以及cur指针,都向后移动一个块*/  
  28.                 prev = cur;  
  29.                 cur = aligned;  
  30.                 avail = slob_units(cur);//重新获取单元数   
  31.             }  
  32.   
  33.             next = slob_next(cur);//获取下一个空闲块   
  34.             if (avail == units) { /* 空闲块的大小和要求的大小完全相符 */  
  35.                 if (prev)//存在先驱块,则将先驱块的指针指向next块   
  36.                     set_slob(prev, slob_units(prev), next);  
  37.                 else//不存在先驱块说明为第一个块,则将free直接指向next   
  38.                     sp->free = next;  
  39.             } else { /* 大小不相符,则要将块分裂*/  
  40.                 if (prev)  
  41.                     set_slob(prev, slob_units(prev), cur + units);  
  42.                 else  
  43.                     sp->free = cur + units;  
  44.                 set_slob(cur + units, avail - units, next);  
  45.             }  
  46.   
  47.             sp->units -= units;//减少slob的单元数   
  48.             if (!sp->units)//单元数为0表明slob没有空闲单元,则从链表中删除   
  49.                 clear_slob_page_free(sp);  
  50.             return cur;  
  51.         }  
  52.         if (slob_last(cur))  
  53.             return NULL;  
  54.     }  
  55. }  
  • 1
  • 2
  • 下一页

相关内容