slab分配器
提供小内存块不是slab分配器的唯一任务。由于结构上的特点,他也用做一个缓存,主要针对经常分配并释放的对象。通过建立slab缓存,内核能够储备一些ixnag,工后续使用,及时子啊初始化状态也是如此。
slab分配器还有两个更进一步的好处
*调用伙伴系统的操作对系统的数据和指令告诉缓存有相当的影响。
*如果数据存储在伙伴系统直接提供的也重,那么器地址总是出现在2的幂次的整数倍附近。这对CPU高速缓存的利用有负面影响,由于这种地址分布,使得某些缓存行过渡使用,而其他的则几乎为空。多处理兄台你可能会加剧这种不理情况,因为不同的内存地址可能子啊不同的总线上传输,上述请阿坤哥会导致某些总线拥塞,而其他总线则几乎没有使用。
slab分配器有何得名?各个缓存管理的对象,会合并为较大的组,覆盖一个或多个连续页帧。这周昂组称为slab,每个缓存由几个这种slab组成。
slab(simple linked list of block)
备选分配器##
slub分配器进行了特别优化,以便减少代码量。他围绕一个简单的内存块链表展开。在分配内存是,使用了同样简单的最先适配算法。
slub分配器通过将页帧打包为组,并通过struct page中未使用的字段来管理这些组,试图最小化所需的内存开销。
注意:有很重要的一点需要强调,内核的其余部分无需关注底层选择使用了那个分配器。所有分配器的前端接口是相同的每个分配器都必须实现一组特定的函数,用于内存分配和缓存。
kmalloc\__kmalloc和kmalloc_node是一般内存分配函数
kmem_cache_alloc\kmem_cache_alloc_node提供特定类型的内核缓存。
内核中的内存管理
内核中一般的内存分配和释放函数 C标准库中等价函数的名称类似,用法也几乎相同。
*kmalloc(size,flags)分配长度为size字节的一个内存区,并返回指向该内存区起始处的一个void指针。如果没有足够内存,则结构为NULL指针。
与用户空间程序设计相比,内核还包括percpu_alloc和percpu_free函数,用于为各个系统CPU分配和释放所需内存区。
*kfree(*ptr)释放*ptr指向的内存区。
info = (struct cdrom_info *)kmalloc(sizeof (struct cdrom_info), GFP_KERNEL)
输出的各列除了包含用于标识各个缓存的字符串名和曾
*缓存中活动对象的数量。
*缓存中对象的总数(一用和未用)
*所管理对象的长度,按字节计算
*一个slab中对象的数量
*每个slab中也的数量。
*活动slab的数量
*在内核决定向缓存分配更多内存时,所分配对象的数量。每次回分配一个较大的内存块,以减少与伙伴系统交互。子啊缩小缓存时,也使用该值作为释放内存块的大小。
slab分配的原理
slab分配器由一个紧密地交织的数据和内存结构的网络组成,初看起来不容易理解其运作方式。
基本上slab缓存两部分组成:保存管理型数据的缓存对象和保存被管理对象的各个slab.
每个缓存只负责一种对象类型,或提供一般性的缓冲区。各个缓存中slab的数目各有不同,这与已经使用的页的数目、对象长度和被管理对象的数目有关。
1.缓存的精细结构
如果我们更仔细研究缓存的结构,就可以注意到一些更重要的细节。
除了管理性数据(如已用和空想对象或标志寄存器的数目),缓存结构包括两个特别重要的成员
*指向一个数组的指针,其中保存了个CPU最后释放的对象。
*每个内存结点都对应3个表头,用于组织slab的链表、
缓存结构指向一个数组,其中包含了与系统CPU数目相同的数组项。每个元素都是一个指针,指向要给进一步的结构称为数组缓存(array cache),其中包含了对应于特定系统CPU的管理数据。管理型数据之后的内存区包含一个指针数组,各个数组想指向slab中未使用的对象。
(1)仍然处于CPU高速缓存中的per-CPU对象
(2)现存slab中未使用的对象
(3)刚使用伙伴系统分配的心slab中卫使用的对象。
2.slab的精细结构
对象在slab中并非连续排序,而是按照一个相当复杂的方案分布的。
用于每个对象的长度并不反应器确切的大小。
*slab创建时使用标志SLAB_HWCACHE_ALLGN,slab用户可以要求对象按硬件缓冲行对齐。
*如果不要求硬件缓存行对齐,那么内核保证对象按BYTES_PER_WORD对齐,该值是表示void指针所需字节的数目。
大多数情况下,slab内存区的长度是不能被对象长度整除的。管理数据可以防止在slab自身,也可以放置到使用kmalloc分配的不同内存中。内核如何选择,取决于slab的长度和一用对象的数量。
page结构包括一个链表元素,用于管理各种链表中的页。对于slab缓存中的页面而言,该指针是不必要的,可用于其他用途。
*page->lru.next指向页驻留的缓存的管理结构
*page->lru.prev指向保存该也的slab的管理结构
设置会或读取slab信息分别由set_page_slab和get_page_slab函数完成,带有_cache后缀的函数则处理缓存信息的设置和读取
mm/slab.c
void page_set_cache
struct kmem_cache *page_get_cache()
void page_set_slab()
struct slab *page_get_slab()
实现
为实现如上所述的slab分配器,使用了各种数据结构。
*危险区(Red Zoning):每个对象的开始和结束增加一个额外的内存区,其中填充一直的字节模式
*对象毒化(Object Poisoning):在建立和释放slab时,将对象用于定义的模式填充。
1.数据结构
每个缓存有kmem_cache结构一个实例表示。
mm/slab.c
struct kmem_cache {
1)per-CPU数据,子啊每次分配/释放期间都会访问
2)可调整的缓存参数,由cache_chain_mutex保护
3)后端每次分配和释放内存时都会访问
4)缓存的增长/缩短
5)缓存创建/删除
}
开始的几个成员涉及每次分配期间内核对特定CPU数据的访问
*array是一个指向数组的指针,每个数组想都对 应与系统中的一个CPU。
*batchcount指定了在per_CPU列表为空的情况下,从缓存的slab中获取对象的数目。
*limit指定了per-CPU列表中保存的对象的最大数目。
*buffer_size指定了缓存中管理的对象的长度
*假定内核有一个指针指向slab中的一个元素,二序要去顶对应的对象索引。
mm/slab.c
struct array_cache {
unsigned int avail;
unsigned int limit;
unsigned int batchcount;
unsigned int touched;
spinlock_t lock;
void *entry[];
}
*nodelists是一个数组,每个数组想对应于系统中一个可能的内存节点。每个数组想都包含struct kemem_list3的一个实例。
*flags是一个标志寄存器,动易缓存的全局性质。、
*objsize是缓存中对象的长度,包括用于对齐目的的所有填充字节
*num保存了可以放入slab的对象的最大数目。
*free_limit指定了缓存在收缩之后空闲对象数的上限。
mm/slab.c
struct kmem_list3 {
struct list_head slabs_partial;
struct list_head slabs_full;
struct list_head slabs_free;
free_objects;
free_limit;
colour_next;
list_lock;
array_cache *shared;
array_chahe **alien;
next_reap;
free_touched;
};
*gfporder指定了slab包含用于增长缓存的所有变量。
*3个colour成员包含了slab着色相关的所有数据额。
*如果slab头部的管理数据存储在slab外部,则slabp_cache指向所需内存的一般性缓存。如果slab头部在slab上,则slabp_cache为NULL指针。
*dflags是龄一个标志结合,描述slab的动态性质,但当前没有定义标志。
*ctor是一个指针,指向在对象创建时调用额构造函数。
2.初始化
初看起来,slab系统的初始化不是特别麻烦,因为伙伴系统已经玩去哪启用,内核没有收到其他特别的限制。
为初始化slab数据结构,内核需要若干源小于一整夜的内存块,这些最适由kmalloc分配。
kmem_cache_init函数用于初始化slab分配器。他在内核初始化阶段(start_kernel)\伙伴系统启用之后调用。
(1)kmem_cache_init创建系统中的第一个slab缓存,以便为kmem_cache的实例提供内存。
(2)kmem_cache_init接下来初始化一般性的缓存,用作 kamlloc内存的来源。
mm/slab.c
struct arraycache_init {
struct array_cache cache;
void *entries[BOOT_CPUCACHE_ENTRIES];
}
(3)在kmem_cache_init的最后一步,吧到现在为止一直使用的数据结构的所有静态实例化成员,用kmalloc动态分配的版本替换。
3.创建缓存
创建新的slab缓存必须调用kmem_cache_create.该函数需要很多参数。
mm/slab.c
strcut kmem_cache * kmem_cache_create ();
除了刻度的name随后会出现/proc/slabinfo,该函数需要被管理对象以字节的长度,子啊对齐数据使用的偏移量(align,几乎所有的情形下都是0),flags中是一组标志,而ctor和dtor中是构造/析构函数。
*一个slab管理15个对象(num=15)
*使用一个页面(gfp_oreder = 0);
*有5种可能的颜色,每种颜色使用32字节的偏移量
*slab头存储在slab上。
*必须产生per_CPU缓存。
为各个处理器分配所需的内存:一个array_cache的实例和一个指针数组,数组想数目在上述的计算中给出;并初始化数据结构,这些任务委托给do_tune_cpucache>
*为完成初始化,将初始化郭额kmem_cache实例条件到全局链表,表头为cache_chain,定义在mm/slab.c中。
4 分配对象
kmem_cache_alloc用于从特定的缓存获取对象。器结果可能是指向内存区额指针,也可能分配失败,返回NULL指针。该函数需要两个参数:用于获取对象的缓存,以及精确描述分配特征的标志变量。
通用缓存
传统意义上的分配/释放内存,则必须调用kmalloc和kfree函数。这个两个函数,相当于用户空间中C标准库malloc和free函数的内核等价物。
1kmalloc的实现