法律声明:《linux 3.4.10 内核内存管理源代码分析》系列文章由机器人(ancjf@163.com)发表于http://blog.csdn.net/ancjf,文章遵循GPL协议。欢迎转载,转载请注明作者和此条款。
Slab分配器
伙伴系统一次内存分配最少都是一页,实际上很多时候我们分配内存的时候都只是一小块,不需要一页这样大的空间。这时候我们可以从slab来分配内存,Slab是伙伴系统之上的一个内存分配器,设计slab的目的是避免伙伴系统的一些缺陷。
内存分配的实质就是根据申请者申请的参数查找一块合适的内存,然后把内存地址返回。伙伴系统是最底层的内存管理系统,Slab是在伙伴系统之上的一个内存分配器,slab把从伙伴系统申请的内存块分割成更小的块来管理,分配的时候查找一个合适的块返回首地址。
slab从伙伴系统申请的内存块用struct slab描述,但Slab并不是直接基于struct slab来管理内存,因为还有几个需要考虑的因素:
一:在多cpu环境下,访问一个变量需要获得回环锁,如果对每个cpu缓存一些内存空间,这样可以提高性能。Slab采取的方法是对每个cpu缓存一些对象,如果cpu缓存的对象为空了则一次从获取一批对象缓存起来。为了这个因素,定义了结构struct array_cache。
二:在numa系统上,每个节点上的访问速度可能不一样,分配时候需要选择合适节点来进行分配,对slab块也应该能按节点管理。针对这个因素定义了结构structkmem_list3。
三:能同时对多个slab块进行管理,可以提高程序灵活性。在structkmem_list3中设定了三个链表分别是块,闲,和半空slab块链表。
处于slab顶层的是slab缓存,slab缓存用struct kmem_cache描述。上面的结构都在struct kmem_cache中组织起来。每次内存分配都是在一个slab缓存中进行的,一个slab缓存中分配的对象的长度都是相同的。
下面是这些结构的定义:
struct slab定义如下:
struct slab {
union {
struct {
struct list_headlist; //一个缓存中的所有slab块组成的链表
unsigned longcolouroff; //着色区的大小
void *s_mem; //指向对象区的起点
unsigned int inuse; //Slab中所分配对象的个数
kmem_bufctl_t free; //指向空闲对象链中的第一个对象
unsigned shortnodeid; //slab块所在结点的结点号
};
struct slab_rcu__slab_cover_slab_rcu;
};
};
structarray_cache定义如下:
structarray_cache {
unsigned int avail; //当前缓存的对象数目
unsigned int limit; //最多可以缓存的对象数目
unsigned int batchcount; //缓存对象空的情况下一次获取的对象数
unsigned int touched; //用来表示这个对象最新是否被使用
spinlock_t lock; //回环锁
void *entry[]; //指向缓存对象数组
};
structkmem_list3定义如下:
structkmem_list3 {
struct list_head slabs_partial; //分配空的slab块链表
struct list_head slabs_full; //没有空闲对应的slab块链表
struct list_head slabs_free; //空闲的slab块链表
unsigned long free_objects; //空闲对象数
unsigned int free_limit; //空闲对象数上限
unsigned int colour_next; //下一个颜色
spinlock_t list_lock;
struct array_cache *shared; //共享的对象缓存
struct array_cache **alien; /* on other nodes */
unsigned long next_reap; //定义了内核在两次尝试收缩缓存之间,必须经过的时间间隔
int free_touched; //表示三链表是否是活动的
};
structkmem_cache定义如下:
structkmem_cache {
/* 1) Cachetunables. Protected by cache_chain_mutex */
unsigned int batchcount; //一批对象的数量
unsigned int limit; //空闲对象数上限
unsigned int shared; //共享对象数
unsigned int buffer_size; //对象实际长度
u32 reciprocal_buffer_size; //
/* 2) touched byevery alloc & free from the backend */
unsigned int flags; //一些固定的标志位
unsigned int num; //每个slab块包含的对象数
/* 3)cache_grow/shrink */
/* order of pgs per slab (2^n) */
unsigned int gfporder; //slab块的阶
/* force GFP flags, e.g. GFP_DMA */
gfp_t gfpflags; //分配标志位
size_t colour; //颜色数
unsigned int colour_off; //色差,相邻两种颜色的便宜值的差
struct kmem_cache *slabp_cache; //如果slab块的管理数据是独立的,则在这个缓存中分配空间
unsigned int slab_size;
unsigned int dflags; /* dynamic flags */
/* constructor func */
void (*ctor)(void *obj); //构造函数
/* 4) cachecreation/removal */
const char *name; //缓存名称,在整个系统中是唯一的
struct list_head next; //所有的slab缓存用这个结果连接成一个链表
/* 5) statistics*/
#ifdefCONFIG_DEBUG_SLAB
unsigned long num_active;
unsigned long num_allocations;
unsigned long high_mark;
unsigned long grown;
unsigned long reaped;
unsigned long errors;
unsigned long max_freeable;
unsigned long node_allocs;
unsigned long node_frees;
unsigned long node_overflow;
atomic_t allochit;
atomic_t allocmiss;
atomic_t freehit;
atomic_t freemiss;
/*
* If debugging is enabled, then the allocatorcan add additional
* fields and/or padding to every object.buffer_size contains the total
* object size including these internal fields,the following two
* variables contain the offset to the userobject and its size.
*/
int obj_offset;
int obj_size;
#endif /*CONFIG_DEBUG_SLAB */
/* 6)per-cpu/per-node data, touched during every alloc/free */
/*
* We put array[] at the end of kmem_cache,because we want to size
* this array to nr_cpu_ids slots instead ofNR_CPUS
* (see kmem_cache_init())
* We still use [NR_CPUS] and not [1] or [0]because cache_cache
* is statically defined, so we reserve the maxnumber of cpus.
*/
struct kmem_list3 **nodelists; //指向三链表数组
struct array_cache *array[NR_CPUS]; //对每个cpu,指向每个cpu的对象缓存
/*
* Do not add fields after array[]
*/
};
对象:一块内存空间,用空间的首地址标识。
三链表:对应struct kmem_list3,包含空,满,部分空三个链表。
对象缓存;缓存堆栈:对应struct array_cache,用来缓存一些对象。
缓存;slab缓存:对应struct kmem_cache。