slab分配器

from:slab分配器


每当我们要分配内存的时候,我们会使用malloc,malloc是C语言中分配内存的函数,我们可以使用malloc及其在C标准库中的相关函数,大多数程序分配若干字节内存时,经常会调用这些函数。内核也必须经常分配内存,但无法借助于标准库的函数,之前描述的伙伴系统支持按页分配内存,但这个单位对于内核而言实在太大了。

如果需要为一个10个字符的字符串分配空间,分配一个4KB或更多空间的完整页面,不仅仅浪费而且完全不可接收,所以内核需要将页拆分为更小的单位,以便可以容纳大量的小对象。

为此,内核必须引入新的管理机制,这会给内核带来更大的开销,为了最小化这个额外的负担对系统性能的影响,该管理层的实现应该尽可能的紧凑,以便不要对处理器的高速缓存TLB带来特别的影响。同事,内核还必须保证内存利用的速度和效率。

不仅Linux,而且类似的UNIX和所有其他的操作系统,都需要面对这个问题,经过一定的时间,已经提出了一些或好或坏的解决方案,在一般的操作系统的文献中都有讲解。其中slab分配的解决办法对许多特定的要求工作情况而言,都是非常高效的。

提供小内存块不是slab分配器的唯一任务,由于结构上的特点,它也用作一个缓存,主要针对经常分配并释放的对象。通过建立slab缓存,内核能够存储一些对象,供后续使用。即便在初始化状态也是如此。

例如,为管理与进程关联的文件系统数据,内核必须经常生成struct fs_struct的新实例,此类型实例占据的内存块同样需要经常回收1。换句话说,内核趋向于非常有规律地分配并释放大小为sizeof(fs_struct)的内存块。

slab分配器将释放的内存块保存在一个内部列表中,并不能马上返回给伙伴系统。在请求为该类对象分配一个新实例时,会使用最近释放的内存块。这有两个有点,首先,由于内核不必使用伙伴系统算法,处理时间会变短,其次,由于该内存块仍然时新的,因此其仍然驻留在CPU高速缓存的概率较高。

slab分配器还有两个更进一步的好处:

  1. 调用伙伴系统的操作对系统的数据和指令高速缓存有相当的影响。内核越浪费这些资源,这些资源对用户空间进程就越不可用。更轻量级的slab分配器在可能的情况下减少了对伙伴系统的调用,有助于防止不受欢迎的缓存污染。

  2. 如果数据存储在伙伴系统直接提供的页中,那么其地址总是出现在2的幂次数的整数倍附近。这对CPU高速缓存的利用有负面影响,由于这种地址分布,使得某些缓存行过度使用,而其他的则几乎为空。多处理器会更加剧这种不利情况,因为不同的内存地址可能在不同的总线上传输。

通过slab着色(slab coloring),slab分配器能够均匀地分配对象,以实现均匀的缓存利用。着色这个术语时隐喻性的,它与颜色无关,只是表示slab中的对象需要移动的特定偏移量,以便使对象放置到不同的缓存行。

备选分配器


尽管slab分配器对许多可能的工作负荷都工作良好,但也有一些情形,它无法提供最优性能。如果某些计算机处于当前硬件尺度的边界上,这类计算机上使用slab分配会出现一些问题,例如微小的嵌入式系统,备有大量物理内存的大规模并行系统。在大规模系统中,slab分配器所需大量的元数据可能会成为更严重的问题2

为了处理此类情况,内核版本在2.6开发期间,增加了slab分配器的两个替代品,分别为slob分配器和slub分配器。

slob分配器

slob分配器进行了特别的优化,以减少代码量,它围绕一个简单的内存块链表展开。在分配内存时,使用了同样简单的最先适配算法。slob分配器只有大约600行代码,总的代码量很小,但从速度上来说,它并不是最高效的分配器,也无法为大型系统所使用。

slub分配器

slub分配器通过将页帧打包为组,并通过struct page中未使用的字段来管理这些数组,试图最小化所需内存的开销,这样做不会简化该结构的定义,但是在大型计算机上,slub分配器比slab分配器提供了更好的性能。




由于slab分配是大多数内核的默认选项,所以不会详细记录slob或slub分配器。但无论如何,内核都无需关心底层使用什么分配器,任何一种分配器对内核的接口都是相同的。
slab

伙伴系统、通用分配器和内核代码之间的关系

普通内核代码只需要包含slab.h就可以使用内存分配的所有标准内核函数,连编系统会保证使用编译时选择的分配器来满足程序的内存分配请求。


  1. 在进程结束的时候。

  2. 在大型系统上仅slab的数据结构就需要很多G字节内存,对嵌入式系统来说,slab分配器代码量和复杂性都太高。