标题:
Linux SLUB 分配器详解(1)简介
[打印本页]
作者:
look_w
时间:
2018-5-23 16:18
标题:
Linux SLUB 分配器详解(1)简介
内核对象缓冲区管理Linux 内核在运行过程中,常常会需要经常使用一些内核的数据结构(对象)。例如,当进程的某个线程第一次打开一个文件的时候,内核需要为该文件分配一个称为 file 的数据结构;当该文件被最终关闭的时候,内核必须释放此文件所关联的 file 数据结构。这些小块存储空间并不只在某个内核函数的内部使用,否则就可以使用当前线程的内核栈空间。同时,这些小块存储空间又是动态变化的,不可能像物理内存页面管理使用的 page 结构那样,有多大内存就有多少个 page 结构,形成一个静态长度的队列。而且由于内核无法预测运行中各种不同的内核对象对缓冲区的需求,因此不适合为每一种可能用到的对象建立一个“缓冲池”,因为那样的话很可能出现有些缓冲池已经耗尽而有些缓冲池中却又大量空闲缓冲区的现象。因此,内核只能采取更全局性的方法。
我们可以看出,内核对象的管理与用户进程中的堆管理比较相似,核心问题均是:如何高效地管理内存空间,使得可以快速地进行对象的分配和回收并减少内存碎片。但是内核不能简单地采用用户进程的基于堆的内存分配算法,这是因为内核对其对象的使用具有以下特殊性:
内核使用的对象种类繁多,应该采用一种统一的高效管理方法。
内核对某些对象(如 task_struct)的使用是非常频繁的,所以用户进程堆管理常用的基于搜索的分配算法比如First-Fit(在堆中搜索到的第一个满足请求的内存块)和 Best-Fit(使用堆中满足请求的最合适的内存块)并不直接适用,而应该采用某种缓冲区的机制。
内核对象中相当一部分成员需要某些特殊的初始化(例如队列头部)而并非简单地清成全 0。如果能充分重用已被释放的对象使得下次分配时无需初始化,那么可以提高内核的运行效率。
分配器对内核对象缓冲区的组织和管理必须充分考虑对硬件高速缓存的影响。
随着共享内存的多处理器系统的普及,多处理器同时分配某种类型对象的现象时常发生,因此分配器应该尽量避免处理器间同步的开销,应采用某种 Lock-Free 的算法。
如何有效地管理缓冲区空间,长期以来都是一个热门的研究课题。90 年代初期,在 Solaris 2.4 操作系统中,采用了一种称为“slab”(原意是大块的混凝土)的缓冲区分配和管理方法,在相当程度上满足了内核的特殊需求。
SLAB 分配器介绍多年以来,Linux 内核使用一种称为 SLAB 的内核对象缓冲区分配器。SLAB 分配器源于 Solaris 2.4 的分配算法,工作于物理内存页框分配器之上,管理特定大小对象的缓存,进行快速而高效的内存分配。
SLAB 分配器为每种使用的内核对象建立单独的缓冲区。Linux 内核已经采用了伙伴系统(Buddy System)管理物理内存页框,因此 SLAB 分配器直接工作于伙伴系统之上。每种缓冲区由多个 slab 组成,每个 slab就是一组连续的物理内存页框,被划分成了固定数目的对象。根据对象大小的不同,缺省情况下一个 slab 最多可以由 1024 个物理内存页框构成。出于对齐等其它方面的要求,slab 中分配给对象的内存可能大于用户要求的对象实际大小,这会造成一定的内存浪费。
由于内核对象在使用前和释放后可能需要做某些特殊处理,缓冲区拥有自己的“构造函数(constructor)”和“析构函数(destructor)”,类似于C++ 等面向对象编程语言中的概念(不过最新版本的 SLAB 分配器取消了析构函数)。创建新 slab 时内核调用构造函数初始化每个对象,释放 slab 时则调用析构函数。这就是内核数据结构被称为对象的原因。
内核使用 kmem_cache 数据结构管理缓冲区。由于 kmem_cache 自身也是一种内核对象,所以需要一个专门的缓冲区。所有缓冲区的 kmem_cache 控制结构被组织成以 cache_chain 为队列头的一个双向循环队列,同时 cache_cache 全局变量指向kmem_cache 对象缓冲区的 kmem_cache 对象。每个 slab 都需要一个类型为 struct slab 的描述符数据结构管理其状态,同时还需要一个 kmem_bufctl_t(被定义为无符号整数)的结构数组来管理空闲对象。如果对象不超过 1/8 个物理内存页框的大小,那么这些 slab 管理结构直接存放在 slab 的内部,位于分配给 slab 的第一个物理内存页框的起始位置;否则的话,存放在 slab 外部,位于由 kmalloc 分配的通用对象缓冲区中。
slab 中的对象有 2 种状态:已分配或空闲。为了有效地管理 slab,根据已分配对象的数目,slab 可以有 3 种状态,动态地处于缓冲区相应的队列中:
Full 队列,此时该 slab 中没有空闲对象。
Partial 队列,此时该 slab 中既有已分配的对象,也有空闲对象。
Empty 队列,此时该 slab 中全是空闲对象。
NUMA(Non Uniform Memory Access) 系统中,每个节点都会拥有这 3 种 slab 队列,struct kmem_list3结构用于维护相关队列。SLAB 分配器优先从 Partial 队列里的 slab 中分配对象。当 slab 的最后一个已分配对象被释放时,该 slab 从 Partial 队列转移到 Empty 队列;当 slab 的最后一个空闲对象被分配时,该 slab 从Partial 队列转移到Full 队列里。缓冲区中空闲对象总数不足时,则分配更多的 slab;但是如果空闲对象比较富余,Empty 队列的部分 slab 将被定期回收。
为了充分利用硬件高速缓存,SLAB 分配器允许对象在一级硬件高速缓存中对齐(创建缓冲区时,设置 SLAB_HWCACHE_ALIGN 标志);同时使用着色(color)策略,使得同一缓冲区内不同 slab 中相同编号的对象的地址相互错开,避免它们被放入同一物理高速缓存行而造成频繁换入/换出的性能损失。
为了支持多处理器同时分配对象,缓冲区为每个处理器维护一个本地缓存。处理器直接从本地缓存中分配对象,从而避免了锁的使用;当本地缓存为空时,从 slab 中批量分配对象到本地缓存。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0