内存管理 ---slab机制 分配对象

从一个缓存中分配对象总是遵循下面的原则:

1.本地高速缓存中是否有空闲对象,如果有的话则从其中获取对象,

2.如果本地高速缓存中没有对象,则从kmem_list3中的slab链表中寻找空闲对象并填充到本地高速缓存再分配;

3.如果所有的slab中都没有空闲对象了,那么就要创建新的slab,再分配 。

来自:http://blog.csdn.net/vanbreaker/article/details/7671211

Linux内核从slab中分配内存空间由kmalloc()或kmem_cache_alloc()函数实现。

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

/**
 * __do_kmalloc - allocate memory
 * @size: how many bytes of memory are required.
 * @flags: the type of memory to allocate (see kmalloc).
 * @caller: function caller for debug tracking of the caller
 */
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
					  void *caller)
{
	struct kmem_cache *cachep;
	void *ret;

	/* If you want to save a few bytes .text space: replace
	 * __ with kmem_.
	 * Then kmalloc uses the uninlined functions instead of the inline
	 * functions.
	 */   /*查找指定大小的通用cache,关于sizes数组,在前面 
    的初始化中就已经分析过了*/  
	cachep = __find_general_cachep(size, flags);
	if (unlikely(ZERO_OR_NULL_PTR(cachep)))
		return cachep;
	ret = __cache_alloc(cachep, flags, caller); /*实际的分配工作*/ 

	trace_kmalloc((unsigned long) caller, ret,
		      size, cachep->size, flags);

	return ret;
}

最后调用实际的分配工作:__do_cache_alloc()->__cache_alloc()->____cache_alloc();

static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
	void *objp;
	struct array_cache *ac;
	bool force_refill = false;

	check_irq_off();

	ac = cpu_cache_get(cachep); /* 获得本CPU的local cache */  
	if (likely(ac->avail)) {/* 如果local cache中有可用的空闲对象 */  
		ac->touched = 1;
<span style="white-space:pre">		 /* 从local cache的entry数组中提取最后面的空闲对象 */  </span>
		objp = ac_get_obj(cachep, ac, flags, false); 

		/*
		 * Allow for the possibility all avail objects are not allowed
		 * by the current flags
		 */
		if (objp) {
			STATS_INC_ALLOCHIT(cachep);
			goto out;
		}
		force_refill = true;  //标志位 是否需要refill
	}

	STATS_INC_ALLOCMISS(cachep); /* 从slab三链中提取空闲对象填充到local cache中 */  
	objp = cache_alloc_refill(cachep, flags, force_refill);
	/*
	 * the 'ac' may be updated by cache_alloc_refill(),
	 * and kmemleak_erase() requires its correct value.
	 */ /* cache_alloc_refill的cache_grow打开了中断,local cache指针可能发生了变化,需要重新获得
	ac = cpu_cache_get(cachep);

out:
	/*
	 * To avoid a false negative, if an object that is in one of the
	 * per-CPU caches is leaked, we need to make sure kmemleak doesn't
	 * treat the array pointers as a reference to the object.
	 */
	if (objp)
		kmemleak_erase(&ac->entry[ac->avail]);/* 分配出去的对象,其entry指针指向空 */  
	return objp;
}
static inline void *ac_get_obj(struct kmem_cache *cachep,
			struct array_cache *ac, gfp_t flags, bool force_refill)
{
	void *objp;

	if (unlikely(sk_memalloc_socks()))
		objp = __ac_get_obj(cachep, ac, flags, force_refill);
	else
<span style="white-space:pre">	 /*先将avail的值减1,这样avail对应的空闲对象是最热的,即最近释放出来的, 
          更有可能驻留在CPU高速缓存中*/  </span>
	objp = ac->entry[--ac->avail];

	return objp;
}

核心:

tatic void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
							bool force_refill)
{
	int batchcount;
	struct kmem_list3 *l3;
	struct array_cache *ac;
	int node;

	check_irq_off();
	node = numa_mem_id(); /* 获得本内存节点,UMA只有一个节点 */  
	if (unlikely(force_refill))
		goto force_grow;
retry:
	ac = cpu_cache_get(cachep);
	batchcount = ac->batchcount; /*获取批量转移的数目*/  
	if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
		/*
		 * If there was little recent activity on this cache, then
		 * perform only a partial refill.  Otherwise we could generate
		 * refill bouncing.
		 */   /* 最近未使用过此local cache,没有必要添加过多的对象 
         ,添加的数目为默认的限定值 */  
		batchcount = BATCHREFILL_LIMIT;
	}
	l3 = cachep->nodelists[node];/*获取kmem_list3*/  

	BUG_ON(ac->avail > 0 || !l3);
	spin_lock(&l3->list_lock);

	/* See if we can refill from the shared array */
<span style="white-space:pre">	 /* shared local cache用于多核系统中,为所有cpu共享 
    ,如果有共享本地高速缓存 
    ,那么首先从shared local cache中批量搬运空闲对象到local cache中 
    。通过shared local cache使填充工作变得简单。*/  </span>
	if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) {
		l3->shared->touched = 1;
		goto alloc_done;
	}
<span style="white-space:pre">		  /* 如果没有shared local cache,或是其中没有空闲的对象 
    ,从slab链表中分配 */  </span>
	while (batchcount > 0) {
		struct list_head *entry;
		struct slab *slabp;
		/* Get slab alloc is to come from. */
<span style="white-space:pre">		/*扫描slab链表,先从partial链表开始,如果整个partial链表都无法找到batchcount个空闲对象, 
       </span>    <span style="white-space:pre"> 再扫描free链表*/ </span>
		entry = l3->slabs_partial.next;
		if (entry == &l3->slabs_partial) { /*entry回到表头说明partial链表已经扫描完毕,开始扫描free链表*/ 
			l3->free_touched = 1; /* 表示刚刚访问了slab空链表 */  
			entry = l3->slabs_free.next;
			if (entry == &l3->slabs_free)/* 空slab链表也为空,必须增加slab了 */  
				goto must_grow;
		}

		slabp = list_entry(entry, struct slab, list);
		check_slabp(cachep, slabp);
		check_spinlock_acquired(cachep);

		/*
		 * The slab was either on partial or free list so
		 * there must be at least one object available for
		 * allocation.
		 */
		BUG_ON(slabp->inuse >= cachep->num);
<span style="white-space:pre">		  /*如果slabp中还存在空闲对象并且还需要继续填充对象到本地高速缓存*/  </span>
		while (slabp->inuse < cachep->num && batchcount--) {
			STATS_INC_ALLOCED(cachep);
			STATS_INC_ACTIVE(cachep);
			STATS_SET_HIGH(cachep);
<span style="white-space:pre">			 /*填充的本质就是用ac后面的void*数组元素指向一个空闲对象</span>
<span style="white-space:pre">				 ac->entry[ac->avail++] = slab_get_obj(cachep, slabp, node); </span>
<span style="white-space:pre">				 */  </span>
			ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp,node));
		}
		check_slabp(cachep, slabp);

		/* move slabp to correct slabp list: */
		list_del(&slabp->list);
		if (slabp->free == BUFCTL_END) /*free等于BUFCTL_END表示空闲对象已耗尽,将slab插入full链表*/  
			list_add(&slabp->list, &l3->slabs_full);
		else
			list_add(&slabp->list, &l3->slabs_partial);
	}

must_grow:
<span style="white-space:pre">	 /* 前面从slab链表中添加avail个空闲对象到local cache中 
    ,更新slab链表的空闲对象数 */ </span>
	l3->free_objects -= ac->avail;
alloc_done:
	spin_unlock(&l3->list_lock);

	if (unlikely(!ac->avail)) { /* local cache中仍没有可用的空闲对象,说明slab 
                             三链中也没有空闲对象,需要创建新的空slab了 */ 
		int x;
force_grow:
		x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL); /* 创建一个空slab */  

		/* cache_grow can reenable interrupts, then ac could change. */
		ac = cpu_cache_get(cachep);
		node = numa_mem_id();

		/* no objects in sight? abort */
		if (!x && (ac->avail == 0 || force_refill))
			return NULL;

		if (!ac->avail)		/* objects refilled by interrupt? */
			goto retry;
	}
	ac->touched = 1;

	return ac_get_obj(cachep, ac, flags, force_refill); /* 返回local cache中最后一个空闲对象的虚拟地址 objp = ac->entry[--ac->avail];*/  
}

辅助函数:

/*
 * Transfer objects in one arraycache to another.
 * Locking must be handled by the caller.
 *
 * Return the number of entries transferred.
 */
static int transfer_objects(struct array_cache *to,
		struct array_cache *from, unsigned int max)
{
	/* Figure out how many entries to transfer */
	int nr = min(min(from->avail, max), to->limit - to->avail);

	if (!nr)
		return 0;
	/*拷贝*/
	memcpy(to->entry + to->avail, from->entry + from->avail -nr,
			sizeof(void *) *nr);
	/*两边数据更新*/
	from->avail -= nr;
	to->avail += nr;
	to->touched = 1;
	return nr;
}
/*从slab中提取一个空闲对象*/
static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,
				int nodeid)
{
	/* 获得一个空闲的对象,free是本slab中第一个空闲对象的索引 */
	void *objp = index_to_obj(cachep, slabp, slabp->free);
	kmem_bufctl_t next;
 	/* 更新在用对象计数 */
	slabp->inuse++;
 	/* 获得下一个空闲对象的索引 */
	next = slab_bufctl(slabp)[slabp->free];
#if DEBUG
	slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
	WARN_ON(slabp->nodeid != nodeid);
#endif
	/* free指向下一个空闲的对象 */
	slabp->free = next;

	return objp;
}
static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab,
				 unsigned int idx)
{	/* s_mem是slab中第一个对象的起始地址,buffer_size是每个对象的大小
	,这里根据对象索引计算对象的地址 */
	return slab->s_mem + cache->buffer_size * idx;
}
static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)
{
	return (kmem_bufctl_t *) (slabp + 1);
}
static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
{
	return cachep->array[smp_processor_id()];
}

对于cache_grow 扩容 以后分析;

时间: 2024-08-01 12:20:24

内存管理 ---slab机制 分配对象的相关文章

内存管理---slab机制 销毁对象

Linux内核中将对象释放到slab中上层所用函数为kfree()或kmem_cache_free().两个函数都会调用__cache_free()函数. 缓存回收对象基于以下原则 1.本地高速缓存的空间还可以容纳空闲对象,则直接将对象放回本地高速缓存 2.本地高速缓存的空间已满,则按batchcount的值将对象从本地高速缓存转移到slab中,转移是基于先进先出的原则的,也就是转移entry数组最前面的batchcount个空闲对象,因为这些对象在数组中存在的时间相对较长,不太可能仍然驻留在C

JVM内存管理的机制

1.JVM内存管理的机制 内存空间划分为:Sun JDK在实现时遵照JVM规范,将内存空间划分为堆.JVM方法栈.方法区.本地方法栈.PC寄存器. 堆: 堆用于存储对象实例及数组值,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中对象所占用的内存由GC进行回收,在32位操作系统上最大为2GB,在64位操作系统上则没有限制,其大小可通过-Xms和-Xmx来控制,-Xms为JVM启动时申请的最小Heap内存,默认为物理内存的1/64但小于1GB:-Xmx为JVM可申请的最大He

Nginx之共享内存与slab机制

1. 共享内存 在 Nginx 里,一块完整的共享内存以结构体 ngx_shm_zone_t 来封装,如下: typedef struct ngx_shm_zone_s ngx_shm_zone_t; typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data); typedef struct { /* 执行共享内存的起始地址 */ u_char *addr; /* 共享内存的长度 */ size_t s

STL内存管理器的分配策略

STL提供了很多泛型容器,如vector,list和map.程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写.例如可以通过以下语句创建一个vector,它实际上是一个按需增长的动态数组,其每个元素的类型为int整型: stl::vector<int> array; 拥有这样一个动态数组后,用户只需要调用push_back方法往里面添加对象,而不需要考虑需要多少内存: array.push_back(10); a

Linux内存管理2---段机制

1.前言 本文所述关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识讲座的整理. 本讲座主要分三个主题展开对内存管理进行讲解:内存管理的硬件基础.虚拟地址空间的管理.物理地址空间的管理. 本文将主要以X86架构为例来介绍Linux内存管理的段机制. 2.段机制 段是虚拟地址空间的基本单位 段机制必须把虚拟地址空间的一个地址转换为线性地址空间的一个线性地址 可以从三个方面来描述段(如下图) 段的基地址(Base):在线性地址空间中段的起始地址: 段的界限(Limit):在虚拟地址空间中,

JVM堆内存管理与自定义分配参数详解

堆内存模型: 在Java中,堆被划分成两个不同的区域:新生代(Young),老年代(Old).而Permanent属于永久代(方法区),不属于堆内存.新生代又被分为了三个区域:Eden,from  survivor,to survivor.这样划分的目的是为了使JVM能够更好的管理堆内存中的对象,包括内存分配以及回收. 从图中可以看出: 堆大小=新生代+老年代,默认的,新生代与老年代的比例为1:2(该值可以通过参数-XX:NewRatio指定),即:新生代=1/3堆空间大小,老年代=2/3堆空间

OC_内存管理(二)对象复制、循环引用问题、自动释放池

循环调用: 1.循环引用的问题 两个对象A.B,有可能会出现特殊情况:A中包含B的实例变量:B中也包含A的实例变量,如果这两个实例变量都是强引用(A有着B的实例变量所有权,B也有A的实例变量所有权),然后在两个对象销毁时,会出现A.B都不能正常销毁的情况.下面用代码来演示循环调用问题: ClassA的头文件Class.h代码 #import <Foundation/Foundation.h> //类的前向声明 @class ClassB; //类的前向声明与包含头文件的区别?Q: @inter

Android——内存管理-lowmemorykiller 机制

前段时间碰到一个apk多个process依次开跑,跑到最后一个process的时候,第一个process给kill掉了,虽然第一个process中含有broadcast receive,被kill掉的原因是由于触发到了lowmemorykiller,这样一来apk最后的结果就异常了~ 尝试再三 规避掉了这个问题,记录一下~ 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/47317765本文来自 [jscese]的博客! 概念 a

黑马程序员--Objective-C 内存管理我之见解

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- 内存管理 为什么要进行内存管理? 因为设备的内存空间是有限的,如果一直占用,而不回收空间,内存就会被一直占用,导致内存不足,  系统就会就会报警,严重的可能直接退出程序,因此,在软件开发过程中,需要进行内存管理,以保证高效快速的分配内存,