感受面试的时候常常会被问到这个问题,而后我也学习了一下Memcached的slab机制,发现不少服务器都是使用这种机制来分配内存,因此决定学习一下。面试
首先,先对内存分配中的伙伴系统有初步的了解:算法
在编程和使用的服务器软件中,常常须要分配一组连续的页框,而频繁地申请和释放不一样大小的连续页框,必然致使在已分配页框的内存块中分散了许多小块的空闲页框。这样,即便这些页框是空闲的,但要分配一个大块的连续页框就可能没法知足。
而Linux采用了伙伴系统来解决上述难题。把全部的空闲页框分组为11个块链表,每一个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大能够申请1024个连续页框,对应4MB大小的连续内存。每一个页框块的第一个页框的物理地址是该块大小的整数倍。例如,大小为16个页框的块,其起始地址是16×212的倍数。
假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,若是没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另一个移到256个页框的链表中。若是512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,若是仍然没有,则返回错误。
页框块在释放时,内核会主动将两个互为伙伴的页框块合并为一个较大的页框块,成功后会试图寻找伙伴并合并为更大的内存块,直至块的大小超过上限或者没有伙伴为止。互为伙伴的两个内存块必须符合如下条件:
一、两个块具备相同的大小;
二、两个块的物理地址连续;
三、第一个快的物理地址是两个块大小的整数倍。
slab分配机制则是对伙伴算法的改进,slab(Slab Allocation)的设计理念是基于对象缓冲的,基本想法是避免重复大量的初始化和清理操做。slab主要能够用于频繁非配释放的内存对象。替代malloc/free
改进的地方在于:
它对内存区的处理并不须要进行初始化或回收。出于效率的考虑,Linux并不调用对象的构造或析构函数,而是把指向这两个函数的指针都置为空。Linux中引入Slab的主要目的是为了减小对伙伴算法的调用次数。编程
实际上,内核常常反复使用某一内存区。例如,只要内核建立一个新的进程,就要为该进程相关的数据结构(task_struct、打开文件对象等)分配内存区。当进程结束时,收回这些内存区。由于进程的建立和撤销很是频繁,所以,Linux的早期版本把大量的时间花费在反复分配或回收这些内存区上。从Linux2.2开始,把那些频繁使用的页面保存在高速缓存中并从新使用。 能够根据对内存区的使用频率来对它分类。对于预期频繁使用的内存区,能够建立一组特定大小的专用缓冲区进行处理,以免内碎片的产生。对于较少使用的内存区,能够建立一组通用缓冲区(如Linux2.0中所使用的2的幂次方)来处理,即便这种处理模式产生碎片,也对整个系统的性能影响不大。缓存
硬件高速缓存的使用,又为尽可能减小对伙伴算法的调用提供了另外一个理由,由于对伙伴算法的每次调用都会“弄脏”硬件高速缓存,所以,这就增长了对内存的平均访问次数。服务器
Slab分配模式把对象分组放进缓冲区数据结构
对于小对象, 就把Slab的描述结构slab_t放在该Slab中;对于大对象,则把Slab结构游离出来,集中存放。关于Slab中的着色区再给予具体描述:
每一个Slab的首部都有一个小小的区域是不用的,称为“着色区(coloring area)”。着色区的大小使Slab中的每一个对象的起始地址都按高速缓存中的”缓存行(cache line)”大小进行对齐(80386的一级高速缓存行大小为16字节,Pentium为32字节)。由于Slab是由1个页面或多个页面(最多为32)组成,所以,每一个Slab都是从一个页面边界开始的,它天然按高速缓存的缓冲行对齐。可是,Slab中的对象大小不肯定,设置着色区的目的就是将Slab中第一个对象的起始地址日后推到与缓冲行对齐的位置。由于一个缓冲区中有多个Slab,所以,应该把每一个缓冲区中的各个Slab着色区的大小尽可能安排成不一样的大小,这样可使得在不一样的Slab中,处于同一相对位置的对象,让它们在高速缓存中的起始地址相互错开,这样就能够改善高速缓存的存取效率。
每一个Slab上最后一个对象之后也有个小小的废料区是不用的,这是对着色区大小的补偿,其大小取决于着色区的大小,以及Slab与其每一个对象的相对大小。但该区域与着色区的总和对于同一种对象的各个Slab是个常数。
每一个对象的大小基本上是所需数据结构的大小。只有当数据结构的大小不与高速缓存中的缓冲行对齐时,才增长若干字节使其对齐。因此,一个Slab上的全部对象的起始地址都必然是按高速缓存中的缓冲行对齐的。
参考:
深刻分析
Linux
内核源码 http://oss.org.cn/kernel-book/
Memcached 源码分析