分类:
2012-05-18 11:38:32
原文地址:Linux内存管理之slab分配器分析 作者:xgr180
一:准备知识:
前面我们分析过了大内存分配的实现机制,事实上,若为小块内存而请求整个页面,这样对于内存来说是一种极度的浪费。因此linux采用了slab来管理小块内存的分配与释放。Slab最早是由sun的工程师提出。它的提出是基于以下因素考虑的:
1:内核函数经常倾向于反复请求相同的数据类型。比如:创建进程时,会请求一块内存来存放mm结构。
2:不同的结构使用不同的分配方法可以提高效率。同样,如果进程在撤消的时候,内核不把mm结构释放掉,而是存放到一个缓冲区里,以后若有请求mm存储空间的行为就可以直接从缓冲区中取得,而不需重新分配内存.
3:前面我们曾分析过,如果伙伴系统频繁分配,释放内存会影响系统的效率,以此,可以把要释放到的内存放到缓冲区中,直至超过一个阀值才把它释放至伙伴系统,这样可以在一定程度上缓减减伙伴系统的压力
4:为了缓减“内碎片”的产生,通常可以把小内存块按照2的倍数组织在一起,这一点和伙伴系统类似
二:slab分配器概貌:
Slab将缓存分为两种:一种是专用高速缓存,另外一种是普通高速缓存。请注意,这里所说的高速缓存和硬件没有必然的关系,它只是slab分配器中的一个软件概念。
专用高速缓存中用来存放内核使用的数据结构,例如:mm,skb,vm等等
普通高速缓存是指存放一般的数据,比如内核为指针分配一段内存
所有的高速缓存区都通过链表的方式组织在一起,它的首结点是cache_chain
另外,普通高速缓存将分配区分为32*(2^0),32*(2^1),32*(2^2) ….32*(2^12)大小,共13个区域大小,另外,每个大小均有两个高速缓存,一个为DMA高速缓存,一个是常规高速缓存。它们都存放在一个名这cache_size的表中.
Slab分配器把每一个请求的内存称之为对象(和oop设计方法中的对象类似,都有初始化与析构).对象又存放在slab中.slab又按照空,末满,全满全部链接至高速缓存中.如下图所示:
三:slab分配器相关的数据结构:
缓冲区 slab slab 对象
高速缓存:
typedef struct kmem_cache_s kmem_cache_t;
struct kmem_cache_s {
struct array_cache *array[NR_CPUS];/*per cpu结构,每次分配与释放的时候都先从这里取值与存值*/
unsigned int batchcount; /*array[NR_CPUS]中没有空闲对象时,一次为数组所分配的对象数*/
unsigned int limit; /* array[NR_CPUS]中所允许的最大空闲数 */
struct kmem_list3 lists; /*将在后面分析*/
unsigned int objsize; /*slab中的对象大小*/
unsigned int flags; /* cache标志*/
unsigned int num; /*每个slab中的对象数量 */
unsigned int free_limit; /* upper limit of objects in the lists */
spinlock_t spinlock;
unsigned int gfporder; /*2^gfporder即为cache中slab的大小*/
unsigned int gfpflags;
size_t colour; /*着色机制,后面会详细分析 */
unsigned int colour_off; /* colour offset */
unsigned int colour_next; /* cache colouring */
kmem_cache_t *slabp_cache;/*slab描述符放在缓存区外面时,此值指向描述符在普通缓存区中的位置*/
unsigned int slab_size; /*每一个slab的大小*/
unsigned int dflags; /* dynamic flags */
void (*ctor)(void *, kmem_cache_t *, unsigned long); /*构造函数*/
void (*dtor)(void *, kmem_cache_t *, unsigned long); /*析构函数*/
const char *name; /*cache的名字*/
struct list_head next; /*下一个cache 用来构成链表*/
/* 5) statistics */
#if STATS
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;
atomic_t allochit;
atomic_t allocmiss;
atomic_t freehit;
atomic_t freemiss;
#endif
#if DEBUG
int dbghead;
int reallen;
#endif
}
这里值得注意的地方是,与2.4相比,slab部份的结构与成员位置发生了很大改变。一般来说经常用的成员放在结构体的前面。后面会解述为什么。