日期:2014-05-16 浏览次数:20778 次
对象的分配过程体现了内存管理器对内存对象的组织方式,相较Slab分配器,Slub在组织对象的方式上给人的感觉就是简洁精悍。Slub没有用任何的管理区数组来组织这些对象,而是巧妙的将对象之间联系的桥梁嵌入在对象自身之中,因为请求分配对象的程序并不关心在对象分配之前,内存中的内容是什么,而这座桥梁就是空闲对象指针void*,它用以指明下一个空闲对象的地址。void*在对象内存中的偏移为offset,需要注意的一点是,struct kmem_cache和struct kmem_cache_cpu这两个结构中都存在offset变量,两个offset都是表示空闲对象指针的偏移,只不过前者是以字节为单位而后者是以字长为单位!
另外,Slab和Slub都引入了本地CPU缓存的概念,但是Slab分配器中的本地CPU缓存中的对象都是以batchcount为大小,从slab中转移填充(或释放)的,这增加了操作上的复杂性,而Slub则是直接引入一个slab作为本地CPU缓存的管理单位,这样更为简洁。在Slub分配器中,如果一个slab位于本地CPU缓存上的话则称其处于冻结状态(frozen),如果处于slab 链表中中,则称其处于解冻状态(unfrozen)。
下面就来看具体的代码,函数void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)用于在指定的缓存中分配一个对象,它实质上是对函数slab_alloc()的一个封装,我们从slab_alloc()开始进行分析。
我们捡主要的操作进行分析。
首先要从本地CPU高速缓存中分配,那么当然得获取这个结构保存在变量c中。然后出现了两种情况:
1.c->freelist指向NULL,表示本地CPU高速缓存中已经没有可用的空闲对象了,另外一个不想见到的情况就是节点不匹配,出现这两种情况中的任意一种都要通过__slab_alloc()走一条慢速分配的路径;
2.没有异常情况,可以直接从本地CPU高速缓存中进行分配,我们先拿这个来分析。
我们可以看到分配的操作非常的简洁,
object = c->freelist;
c->freelist = object[c->offset];
第一句话很容易理解,freelist指向了一个最热的对象,这一句话就是将这个对象的地址保存在object中。
第二句话的意义也很明显,就是将下一个空闲对象的地址保存在freelist中,以便下次分配使用。但这是如何实现的呢?注意object的定义是void **,这里似乎有点复杂,为什么要定义成一个二级指针?这是为了能够以字长为单位来访问到偏移为offset处的内存值,因为void *指针占用的存储空间是一个字长!可以将object理解成一个void*指针数组的起始地址(数组名),这时在数组内做偏移的运算时就是以void *为单位的,取出来的数值也是以void*为单位的,也就是说都是基于字长的。假如object定义成void*指针,那么虽然仍然可以找到下一个对象的地址,但是却不方便将这个字长的地址值给取出来(必须得将结果强制转换成void**再取内容)!总而言之,这种实现方法是最简洁雅观的。
接下来我们再看慢速分配路径究竟做了什么。