内核中的几种内存分配器

内存管理是内核是最复杂同时也是最重要的一部分,其中就涉及到了多种内存分配器,如果内核初始化阶段使用的bootmem分配器,分配大块内存的伙伴系统,以及其分配较小块内存的slab、slub和slob分配器。

1.bootmem分配器

bootmem分配器用于在启动阶段早期分配内存。该分配器用一个位图来管理页,位图比特位的数目与系统中物理内存页的数目相同。比特位为1表示已用页,比特位为0,表示空闲页。在需要分配内存时,分配器逐位扫描位图,直至找到一个能提供足够连续页的位置,即所谓的最先最佳或最先适配位置。

该分配提供了如下内核接口:

内核接口

说明

alloc_bootmem

alloc_bootmem_pages(size)

按指定大小在ZONE_NORMAL内存域分配内存

alloc_bootmem_low

alloc_bootmem_low_pages(size)

功能同上,只是从ZONE_DMA内存域分配内存。

free_bootmem

释放内存

每个分配器必须实现一组特定的函数,用于内存分配和缓存:

kmalloc、__kmalloc和kmalloc_node是一般的内存分配函数。

kmem_cache_alloc、kmem_cache_alloc_node提供特定类型的内核缓存。

2.slab分配器

功能:提供小的内存块,也可用作一个缓存。

分配和释放内存在内核代码上很常见。为了使频繁分配和释放内存所导致的开销尽量变小,程序员通常使用空闲链表。当分配的内在块不再需要时,将这块内存插入到空闲链表中,而不是真正的释放掉,这种空闲链表相当于内存块缓冲区。但这种方法的不足之处是,内核没有一种手段能够全局地控制空闲链表的大小,实时地更新这些空闲链表的大小。事实上,内核根本也不可能知道有多少空闲链表存在。

为了解决上述问题,内核心提供了slab层或slab分配器。它作为一个通用的内核数据结构缓冲层。slab层使用了以下几个基本原理:

经常使用的数据结构一般来说会被经常分配或释放,所以应该缓存它们。

频繁地分配和释放内存会导致内在碎片(不能找到合适的大块连续的物理地址空间)。为了防止这种问题,缓冲后的空闲链表被存放到连续的物理地址空间上。由于被释放的数据结构返回到了空闲链表,所以没有导致碎片。

在频繁地分配和释放内存空间在情况下,空闲链表保证了更好的性能。因为被释放的对象空间可立即用于下次的分配中。

如果分配器能够知道诸如对象大小、页大小和总的缓冲大小时,它可以作出更聪明的决定。

如果部分缓冲区对每-CPU变量,那么,分配和释放操作可以不需要SMP锁。

如果分配器是非一致内存,它能从相同的内存结点中完成分配操作。

存储的对象可以被着色,以防止多个对象映射到同一个缓冲。

linux中的slab层就是基于上述前提而实现的。

slab层将不同的对象进行分组,称之为“缓冲区(cache)”。一个缓冲区存储一种类型的对象。每种类型的对象有一个缓冲区。kmalloc()的实现就是基于slab层之上的,使用了一族通用的缓冲区。这些缓冲区被分成了一些slab。这些slab是由一个或多个物理上连续的页组成的。每个缓冲区可包含多个slab。

每个slab包含有一些数量的对象,也即被缓冲的数据结构。每个slab问量处于三种状态之间:满、部分满、空。当内核请求一个新的对象时,它总是先查看处于部分满状态的slab,查看是否有合适的空间,如果没有,则空的slab中分配空间。



每个缓冲区由一个kmem_cache结构来表示。该结构包含了三个链表:slabs_full,slabs_partial和slabs_emppty。存储在一个kmem_list3结构中。

slab分配器接口

接口名称

说明

kmem_cache_create

分配一个cache

kmem_cache_destroy

销毁一个cache

kmem_cache_alloc

从一个cache中分配一个对象空间

kmem_cache_free

释放一个对象空间到cache中

这些接口不宜在中断上下文中使用。

旁观者的姓名永远爬不到比赛的计分板上。

内核中的几种内存分配器

相关文章:

你感兴趣的文章:

标签云: