有人可以解释这张关于平板分配的图吗?


10

我试图了解Slab分配的工作原理,以及为什么它与普通分页不同或更好。

发现了该图,如果有更多说明,我认为将对您有所帮助。

一些问题:

  • 3KB和7KB项代表什么?他们必须以某种方式联系起来吗?为什么用这种方式包装?
  • 在缓存列中,缓存是灰色框还是灰色框内的白色/蓝色框?灰色盒子是一堆缓存吗?
  • 这些平板仅仅是蓝框,还是整个“物理连续页面”都是平板?

我真的很感谢您的帮助。谢谢!

平板分配

Answers:


15

我明白你为什么感到困惑。该图有点混乱,实际上可能是不正确的。

首先,让我们考虑一下为什么内核需要低于页面级别的内存分配器。这可能已经是您最了解的东西,但是为了完整起见,我将对其进行介绍。

页是内存操作的典型“单位”。当用户空间应用程序分配内存或对文件进行内存映射或类似的操作时,它通常会获得机器页面大小的倍数。有一些例外。Windows使用64k作为虚拟内存分配单元,而不管CPU的页面大小如何。尽管如此,让我们这样想。

在现代CPU上,就用户空间代码而言,它具有平坦的地址空间。这实际上是虚拟内存系统提供的一种幻觉。操作系统可以从RAM中的任何位置提供页面(如果交换了内存或内存映射的文件,则可能根本不在RAM中),并将页面映射到连续的虚拟地址空间中。

这一切的意义在于,除了操作系统本身的一些特殊情况(也许是DMA缓冲区,也许还有一些在启动时设置的特殊数据结构,哦,还有内核映像本身)之外,操作系统内核可能永远也不必管理大于一页的任何RAM块。这极大地简化了事情,因为这意味着就页面而言,每个分配和释放都具有相同的大小。它还可以有效地消除宏级别的外部碎片。

但是,内核还需要实现自己的某些数据结构,为此,它们需要其他类型的内存分配器。通常可以将这些数据结构视为单个对象的集合(例如,对象可以是“线程”或“互斥体”)。这些对象的大小通常远小于页面大小。

因此,例如,代表进程安全凭证的对象(例如POSIX中的用户ID和组ID)可能只有16个字节左右,而“进程”或“线程”可能最多1kb大小。显然,您不想为这些小记录使用整个页面,因此,其想法是在页面顶部实现一个分配器。

低级分配系统必须满足许多与页面级分配器相同的问题:它必须相当快(包括多核系统上),要最大程度地减少碎片等等。但更重要的是,它应根据要存储的数据结构的类型而可调和可配置。

一些数据结构本质上是“类似缓存的”。例如,许多操作系统维护文件系统对象路径名的高速缓存,以避免目录查找的长链(在Unix中称为“名称高速缓存”或“ namei高速缓存”)。这些对象仅是性能所需的,不是正确性所需的,因此(理论上),如果内存紧张,您可能会忘记整个页面,其中包含所有条目,并且您需要快速释放页面框架。

如果内存紧张,您很快就不需要它们,其他数据结构可以交换到磁盘上。但是您不想使用控制交换的数据结构或虚拟内存系统来做到这一点!

某些数据结构可以在内存中随意移动(例如,如果没有人用指针引用它们),因此可以“紧凑”自身以避免碎片化(如果需要)。

因此,slab分配器的主要思想是页面应仅存储相同“类型”的数据结构。这打勾了所有框:页面中的每个对象都具有相同的大小,因此没有外部碎片。相同“类型”的对象具有相同的性能要求和相同的语义。

顺便说一句,这与分配类似。对于某些类型的对象,如果没有立即可用的内存来分配该对象,则可以等待。一个代表打开文件的对象可能就是一个例子。最好的情况下,打开文件是一项昂贵的操作,因此等待更长的时间不会造成太大的伤害。

对于其他类型的对象(例如,表示必须从现在开始在某个时间发生的实时事件的对象),您真的不想等待。因此,对于某些类型的对象进行过度分配(例如,保留一些空闲页面)是有意义的,这样就可以无需等待就可以满足请求。

您基本上要做的是允许每种类型的对象都有自己的分配器,可以根据该对象的需要对其进行配置。这些按对象分配器被混淆地称为“缓存”。您为每种类型的对象分配一个缓存。(是的,您通常也将实现“缓存缓存”。)每个缓存仅存储相同类型的对象(例如,仅存储线程结构或仅存储地址空间结构)。

每个缓存依次管理“平板”。平板是一个页面框架,其中包含相同类型的对象数组。平板可以是“完整”(正在使用的所有对象),“空”(没有使用的对象)或“部分”(正在使用的某些对象)。

部分平板可能是最有趣的,因为平板分配器为每个部分平板维护一个空闲列表。(完整的平板和空的平板不需要空闲列表。)首先从部分平板分配对象(并且可能首先从“最完整的”部分平板分配对象),以尝试避免分配不需要的页面。

关于平板分配的好处是,可以针对每种对象调整所有这些分配策略选项(以及内存语义)。有些缓存可能会保留一块空的平板,有些则不会。有些可能可以交换到辅助存储,而有些则不能。

Linux具有三种不同的slab分配器,具体取决于您是否需要紧凑性,缓存友好性或原始速度。几年前对此进行了很好的介绍,很好地说明了权衡问题。

Solaris slab分配器(有关详细信息,请参见本文)具有更多细节,可以提高性能。首先,在Solaris中,一切都通过平板分配来完成,包括页面框架分配。(这是Solaris的分配大于半个页面大小的对象的解决方案。)它通过将slab分配器嵌套在slab分配的空间中来管理较小的对象。

Solaris中的某些对象需要复杂且昂贵的构造和销毁操作(例如,具有内核锁的对象),因此它们可能是“部分空闲的”(即构造但未分配)。Solaris还通过在每个CPU上维护空闲列表来优化空闲slab分配,从而确保某些操作完全无需等待。

为了支持通用分配(例如,对于在编译时大小未知的数组),大多数宏内核类型的操作系统还具有表示对象大小而不是对象类型的缓存。例如,FreeBSD为未知对象维护缓存,这些对象的大小为2的幂,从4到256。

我希望您能看到的是,平板分配是一个非常灵活的框架,可以针对不同类型的数据进行调整。它不会与分页竞争,但会对其进行补充(尽管在Solaris中,页面框架是通过平板分配的)。

我希望这有帮助。让我知道是否需要澄清。


6

平板分配器下面的想法是,操作系统需要特定但有些标准的数据结构(例如,过程PCB,信号量,文件元数据等),这些数据结构建议它们必需的内存使用情况。

平板分配器算法提供了存储区域的库存,其存储空间和初始化针对这些标准数据结构进行了优化。例如,从图片中可以看到,可以存在3kb的对象以及7kb的对象。但是,我们知道内核仅以页面大小的倍数分配内存。

为了避免时间和内存浪费,操作系统将不同的缓存区存储在可以根据请求快速分配的存储区中,并且每种对象和结构的缓存大小都不同。但是,缓存不直接包含和管理这些内存区域,甚至不能保证驻留在连续内存中。而是将它们划分为多个连续的内存页,通常选择其数量以减少内存碎片。它是在slab中驻留的不同对象实例,无论是已分配还是已释放。如果高速缓存已满,则会在主内存中的某个位置分配新的平板,并将其添加到高速缓存中。

总而言之,您可以在图片中看到的东西是操作系统已知为3-7(无论kB大小)的对象的示例。内核不会为它们专门分配页面内存,因为这将成倍增加内存碎片,但是会将它们“重定向”到缓存(灰色框)。在高速缓存中,应该有一些地址指示物理上连续的内存区域,这些平板(白色/蓝色的盒子集)和对象最终分配在该平板的一个区域中(蓝色部分,其中没有同一平板的全部类似存储区域)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.