Linux Slob分配器(二)

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

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

void *kmem_cache_alloc_node(struct kmem_cache *c, gfp_t flags, int node)   {       void *b;          if (c->size < PAGE_SIZE) {//对象小于PAGE_SIZE,由Slob分配器进行分配            b = slob_alloc(c->size, flags, c->align, node);           trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,                           SLOB_UNITS(c->size) * SLOB_UNIT,                           flags, node);       } else {//否则通过伙伴系统分配            b = slob_new_pages(flags, get_order(c->size), node);           trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,                           PAGE_SIZE << get_order(c->size),                           flags, node);       }          if (c->ctor)//如果定义了构造函数则调用构造函数            c->ctor(b);          kmemleak_alloc_recursive(b, c->size, 1, c->flags, flags);       return b;   }

由于slob为PAGE_SIZE大小,因此首先要判断要求分配的对象的大小是否在这个范围内,如果是,则通过Slob分配器来分配,否则的话通过伙伴系统分配。

来看Slob分配对象的具体过程

static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)   {       struct slob_page *sp;       struct list_head *prev;       struct list_head *slob_list;       slob_t *b = NULL;       unsigned long flags;          /*根据分配对象的大小选择从哪个链表的slob中进行分配*/       if (size < SLOB_BREAK1)           slob_list = &free_slob_small;       else if (size < SLOB_BREAK2)           slob_list = &free_slob_medium;       else           slob_list = &free_slob_large;          spin_lock_irqsave(&slob_lock, flags);       /* Iterate through each partially free page, try to find room */       list_for_each_entry(sp, slob_list, list) {//遍历slob链表    #ifdef CONFIG_NUMA            /*           * If there's a node specification, search for a partial           * page with a matching node id in the freelist.           */           if (node != -1 && page_to_nid(&sp->page) != node)//节点不匹配                continue;   #endif            /* Enough room on this page? */           if (sp->units < SLOB_UNITS(size))//slob中的空间不够                continue;              /* Attempt to alloc */           prev = sp->list.prev;           b = slob_page_alloc(sp, size, align);//分配对象            if (!b)               continue;              /* Improve fragment distribution and reduce our average           * search time by starting our next search here. (see           * Knuth vol 1, sec 2.5, pg 449) */            /*这里将slob_list链表头移动到prev->next前面,以便下次遍历时能够从prev->next开始遍历*/           if (prev != slob_list->prev &&                   slob_list->next != prev->next)               list_move_tail(slob_list, prev->next);           break;       }       spin_unlock_irqrestore(&slob_lock, flags);          /* Not enough space: must allocate a new page */       if (!b) {//没有分配到对象,也就是说slob_list中没有可以满足分配要求的slob了            b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);//创建新的slob            if (!b)               return NULL;           sp = slob_page(b);//获取slob的地址            set_slob_page(sp);              spin_lock_irqsave(&slob_lock, flags);           sp->units = SLOB_UNITS(PAGE_SIZE);//计算单元数            sp->free = b;    //设置首个空闲块的地址            INIT_LIST_HEAD(&sp->list);           set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));           set_slob_page_free(sp, slob_list);    //将sp链入slob_list            b = slob_page_alloc(sp, size, align);//从新的slob中分配块            BUG_ON(!b);           spin_unlock_irqrestore(&slob_lock, flags);       }       if (unlikely((gfp & __GFP_ZERO) && b))           memset(b, 0, size);       return b;   }  

首先要根据对象的大小来决定从哪个全局链表中寻找slob进行分配 遍历选取的链表,找到一个空间足够满足分配要求的slob 从选取的slob中分配对象块(slob_page_alloc()) 如果遍历完整个链表都没能分配到对象,则创建一个新的slob(slob_new_page()),然后设置slob的属性,再进行分配,可以看到一个新的slob中只有一个块,并且下一个空闲对象的指针指向了下一页的起始处,也就是页对齐的

来看分配的细节操作slab_page_alloc()

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/psydx.html