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

each

 水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7705559      

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

 

  1. <SPAN style="FONT-SIZE: 12px">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. }</SPAN>  
<span style="font-size:12px;">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;
}</span>


由于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. }  
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()

  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. }  
static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)
{
	slob_t *prev, *cur, *aligned = NULL;
	int delta = 0, units = SLOB_UNITS(size);

	for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) {
		slobidx_t avail = slob_units(cur);//计算获取的空闲块的容量

		/*如果设置了对齐值则先将块进行对齐*/
		if (align) {
			aligned = (slob_t *)ALIGN((unsigned long)cur, align);
			delta = aligned - cur;//计算对齐后的对象增加了多少字节的内存
		}

		/*空闲块内存不小于要求分配的 units+对齐增量*/
		if (avail >= units + delta) { /* room enough? */
			slob_t *next;

			/*确实进行了对齐操作*/
			if (delta) { /* need to fragment head to align? */
				next = slob_next(cur);//获取下一个空闲块

				/*这里将原本的一个块分裂成了两个块*/
				set_slob(aligned, avail - delta, next);//设置空闲对象偏移aligned-->next
				set_slob(cur, delta, aligned);//设置空闲对象偏移cur--->aligned

				/*调整prev指针以及cur指针,都向后移动一个块*/
				prev = cur;
				cur = aligned;
				avail = slob_units(cur);//重新获取单元数
			}

			next = slob_next(cur);//获取下一个空闲块
			if (avail == units) { /* 空闲块的大小和要求的大小完全相符 */
				if (prev)//存在先驱块,则将先驱块的指针指向next块
					set_slob(prev, slob_units(prev), next);
				else//不存在先驱块说明为第一个块,则将free直接指向next
					sp->free = next;
			} else { /* 大小不相符,则要将块分裂*/
				if (prev)
					set_slob(prev, slob_units(prev), cur + units);
				else
					sp->free = cur + units;
				set_slob(cur + units, avail - units, next);
			}

			sp->units -= units;//减少slob的单元数
			if (!sp->units)//单元数为0表明slob没有空闲单元,则从链表中删除
				clear_slob_page_free(sp);
			return cur;
		}
		if (slob_last(cur))
			return NULL;
	}
}

几个辅助计算函数:

  1. static slobidx_t slob_units(slob_t *s)  
  2. {  
  3.     if (s->units > 0)  
  4.         return s->units;  
  5.     return 1;  
  6. }  
static slobidx_t slob_units(slob_t *s)
{
	if (s->units > 0)
		return s->units;
	return 1;
}


 

  1. static slob_t *slob_next(slob_t *s)  
  2. {  
  3.     slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK);  
  4.     slobidx_t next;  
  5.   
  6.     if (s[0].units < 0)//s[0].units小于0表明该块是一个小块,其偏移量是用负数表示的   
  7.         next = -s[0].units;  
  8.     else               //否则为大块,s[0]用来存放size,s[1]用来存放下个空闲块的偏移   
  9.         next = s[1].units;  
  10.     return base+next;  
  11. }  
static slob_t *slob_next(slob_t *s)
{
	slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK);
	slobidx_t next;

	if (s[0].units < 0)//s[0].units小于0表明该块是一个小块,其偏移量是用负数表示的
		next = -s[0].units;
	else			   //否则为大块,s[0]用来存放size,s[1]用来存放下个空闲块的偏移
		next = s[1].units;
	return base+next;
}


 

  1. static void set_slob(slob_t *s, slobidx_t size, slob_t *next)  
  2. {  
  3.     slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK);  
  4.     slobidx_t offset = next - base;  
  5.   
  6.     if (size > 1) {//块大小大于1个单元,则要设置两个管理单元   
  7.         s[0].units = size;  
  8.         s[1].units = offset;  
  9.     } else//否则只设置下一个空闲块的偏移,且要取相反数   
  10.         s[0].units = -offset;  
  11. }  
static void set_slob(slob_t *s, slobidx_t size, slob_t *next)
{
	slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK);
	slobidx_t offset = next - base;

	if (size > 1) {//块大小大于1个单元,则要设置两个管理单元
		s[0].units = size;
		s[1].units = offset;
	} else//否则只设置下一个空闲块的偏移,且要取相反数
		s[0].units = -offset;
}


 

  1. static int slob_last(slob_t *s)  
  2. {  
  3.     return !((unsigned long)slob_next(s) & ~PAGE_MASK);  
  4. }  
static int slob_last(slob_t *s)
{
	return !((unsigned long)slob_next(s) & ~PAGE_MASK);
}

这里判断一个空闲块是不是slob中的最后一个空闲块的方法就是看s的下一个空闲块地址是不是页对齐的!

 再回到slob_alloc()中,当成功分配到一个对象块后,要试图将slob_list链表头转移到刚分配出去的块所处的位置。因为从上面的代码中可以看到,分配一个块有可能会因为对齐的原因产生小的碎片,移动链表头可以使得下次分配对象时从产生碎片的地方开始遍历空闲块,尽可能将碎片给分配出去。

当遍历了整个链表都没能获取对象时,则创建新的slob 

  1. static void *slob_new_pages(gfp_t gfp, int order, int node)  
  2. {  
  3.     void *page;  
  4.   
  5. #ifdef CONFIG_NUMA   
  6.     if (node != -1)  
  7.         page = alloc_pages_exact_node(node, gfp, order);  
  8.     else  
  9. #endif   
  10.         page = alloc_pages(gfp, order);  
  11.   
  12.     if (!page)  
  13.         return NULL;  
  14.   
  15.     return page_address(page);  
  16. }  
static void *slob_new_pages(gfp_t gfp, int order, int node)
{
	void *page;

#ifdef CONFIG_NUMA
	if (node != -1)
		page = alloc_pages_exact_node(node, gfp, order);
	else
#endif
		page = alloc_pages(gfp, order);

	if (!page)
		return NULL;

	return page_address(page);
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值