一:准备知识:
前面咱们分析过了大内存分配的实现机制,事实上,若为小块内存而请求整个页面,这样对于内存来讲是一种极度的浪费。所以linux采用了slab来管理小块内存的分配与释放。Slab最先是由sun的工程师提出。它的提出是基于如下因素考虑的:
1:内核函数常常倾向于反复请求相同的数据类型。好比:建立进程时,会请求一块内存来存放mm结构。
2:不一样的结构使用不一样的分配方法能够提升效率。一样,若是进程在撤消的时候,内核不把mm结构释放掉,而是存放到一个缓冲区里,之后如有请求mm存储空间的行为就能够直接从缓冲区中取得,而不需从新分配内存.
3:前面咱们曾分析过,若是伙伴系统频繁分配,释放内存会影响系统的效率,以此,能够把要释放到的内存放到缓冲区中,直至超过一个阀值才把它释放至伙伴系统,这样能够在必定程度上缓减减伙伴系统的压力
4:为了缓减“内碎片”的产生,一般能够把小内存块按照2的倍数组织在一块儿,这一点和伙伴系统相似
二:slab分配器概貌:
Slab将缓存分为两种:一种是专用高速缓存,另一种是普通高速缓存。请注意,这里所说的高速缓存和硬件没有必然的关系,它只是slab分配器中的一个软件概念。
专用高速缓存中用来存放内核使用的数据结构,例如:mm,skb,vm等等
普通高速缓存是指存放通常的数据,好比内核为指针分配一段内存
全部的高速缓存区都经过链表的方式组织在一块儿,它的首结点是cache_chain
另外,普通高速缓存将分配区分为32*(2^0),32*(2^1),32*(2^2) ….32*(2^12)大小,共13个区域大小,另外,每一个大小均有两个高速缓存,一个为DMA高速缓存,一个是常规高速缓存。它们都存放在一个名这cache_size的表中.
Slab分配器把每个请求的内存称之为对象(和oop设计方法中的对象相似,都有初始化与析构).对象又存放在slab中.slab又按照空,末满,全满所有连接至高速缓存中
linux
相當於早期的Lookaside
良好的操做系统性能部分依赖于操做系统有效管理资源的能力。在过去,堆内存管理器是实际的规范,可是其性能会受到内存碎片和内存回收需求的影响。如今,Linux® 内核使用了源自于 Solaris 的一种方法,可是这种方法在嵌入式系统中已经使用了很长时间了,它是将内存做为对象按照大小进行分配。本文将探索 slab 分配器背后所采用的思想,并介绍这种方法提供的接口和用法。
动态内存管理
内存管理的目标是提供一种方法,为实现各类目的而在各个用户之间实现内存共享。内存管理方法应该实现如下两个功能:
最小化管理内存所需的时间
最大化用于通常应用的可用内存(最小化管理开销)
内存管理其实是一种关于权衡的零和游戏。您能够开发一种使用少许内存进行管理的算法,可是要花费更多时间来管理可用内存。也能够开发一个算法来有效地管理内存,但却要使用更多的内存。最终,特定应用程序的需求将促使对这种权衡做出选择。
每一个内存管理器都使用了一种基于堆的分配策略。在这种方法中,大块内存(称为 堆)用来为用户定义的目的提供内存。当用户须要一块内存时,就请求给本身分配必定大小的内存。堆管理器会查看可用内存的状况(使用特定算法)并返回一块内存。搜索过程当中使用的一些算法有 first-fit(在堆中搜索到的第一个知足请求的内存块)和 best-fit(使用堆中知足请求的最合适的内存块)。当用户使用完内存后,就将内存返回给堆。
这种基于堆的分配策略的根本问题是碎片(fragmentation)。当内存块被分配后,它们会以不一样的顺序在不一样的时间返回。这样会在堆中留下一些洞,须要花一些时间才能有效地管理空闲内存。这种算法一般具备较高的内存使用效率(分配须要的内存),可是却须要花费更多时间来对堆进行管理。
另一种方法称为 buddy memory allocation,是一种更快的内存分配技术,它将内存划分为 2 的幂次方个分区,并使用 best-fit 方法来分配内存请求。当用户释放内存时,就会检查 buddy 块,查看其相邻的内存块是否也已经被释放。若是是的话,将合并内存块以最小化内存碎片。这个算法的时间效率更高,可是因为使用 best-fit 方法的缘故,会产生内存浪费。
本文将着重介绍 Linux 内核的内存管理,尤为是 slab 分配提供的机制。
slab 缓存
Linux 所使用的 slab 分配器的基础是 Jeff Bonwick 为 SunOS 操做系统首次引入的一种算法。Jeff 的分配器是围绕对象缓存进行的。在内核中,会为有限的对象集(例如文件描述符和其余常见结构)分配大量内存。Jeff 发现对内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间。所以他的结论是不该该将内存释放回一个全局的内存池,而是将内存保持为针对特定目而初始化的状态。例如,若是内存被分配给了一个互斥锁,那么只需在为互斥锁首次分配内存时执行一次互斥锁初始化函数(mutex_init)便可。后续的内存分配不须要执行这个初始化函数,由于从上次释放和调用析构以后,它已经处于所需的状态中了。
Linux slab 分配器使用了这种思想和其余一些思想来构建一个在空间和时间上都具备高效性的内存分配器。
图 1 给出了 slab 结构的高层组织结构。在最高层是 cache_chain,这是一个 slab 缓存的连接列表。这对于 best-fit 算法很是有用,能够用来查找最适合所须要的分配大小的缓存(遍历列表)。cache_chain 的每一个元素都是一个 kmem_cache 结构的引用(称为一个 cache)。它定义了一个要管理的给定大小的对象池。
图 1. slab 分配器的主要结构
每一个缓存都包含了一个 slabs 列表,这是一段连续的内存块(一般都是页面)。存在 3 种 slab:
slabs_full
彻底分配的 slab
slabs_partial
部分分配的 slab
slabs_empty
空 slab,或者没有对象被分配
注意 slabs_empty 列表中的 slab 是进行回收(reaping)的主要备选对象。正是经过此过程,slab 所使用的内存被返回给操做系统供其余用户使用。
slab 列表中的每一个 slab 都是一个连续的内存块(一个或多个连续页),它们被划分红一个个对象。这些对象是从特定缓存中进行分配和释放的基本元素。注意 slab 是 slab 分配器进行操做的最小分配单位,所以若是须要对 slab 进行扩展,这也就是所扩展的最小值。一般来讲,每一个 slab 被分配为多个对象。
因为对象是从 slab 中进行分配和释放的,所以单个 slab 能够在 slab 列表之间进行移动。例如,当一个 slab 中的全部对象都被使用完时,就从 slabs_partial 列表中移动到 slabs_full 列表中。当一个 slab 彻底被分配而且有对象被释放后,就从 slabs_full 列表中移动到 slabs_partial 列表中。当全部对象都被释放以后,就从 slabs_partial 列表移动到 slabs_empty 列表中。
slab 背后的动机
与传统的内存管理模式相比, slab 缓存分配器提供了不少优势。首先,内核一般依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab 缓存分配器经过对相似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab 分配器还能够支持硬件缓存对齐和着色,这容许不一样缓存中的对象占用相同的缓存行,从而提升缓存的利用率并得到更好的性能。web