SLUB取代了SLAB,成为了默认的内存分配器。内核开发人员称其为:more SMP-friendly SLUB allocator。显然,在桌面平台上的多核心处理器也能从中受益。
SLAB是Linux上一个古老的内存分配器。因为其结构复杂,所以几乎没有人敢修改它,颇似当年我的偶像Anders Hejlsberg用全汇编写成的Delphi编译器,在他离开Borland以后的很长一段时间里,没有敢维护这些编译器的代码。
在研究SLUB之前,先说说SLAB吧。众所周知,操作系统进行内存分配的时候,是以页为单位进行的,也可以称为内存块或者堆。但是内核对象远小于页的大小,而这些对象在操作系统的生命周期中会被频繁的申请和释放,并且实验发现,这些对象初始化的时间超过了分配内存和释放内存的总时间,所以需要一种更细粒度的针对内核对象的分配算法,于是SLAB诞生了:
SLAB缓存已经释放的内核对象,以便下次申请时不需要再次初始化和分配空间,类似对象池的概念。并且没有修改通用的内存分配算法,以保证不影响大内存块分配时的性能。
SLAB最上层为一个由多个kmem_cache组成的cache chain。
每个kmem_cache由slabs_full,slabs_partial,slabs_empty这3个队列组成,分别标记slab全部已被分配的页,部分被分配的页,为分配slab的页。显然,一个新的slab申请到达时,slab_partial页会被考虑;一个内存块释放时,slab_empty将被优先考虑。
由于SLAB按照对象的大小进行了分组,在分配的时候不会产生堆分配方式的碎片,也不会产生Buddy分配算法中的空间浪费,并且支持硬件缓存对齐来提高TLB的性能,堪称完美。
但是这个世界上没有完美的算法,一个算法要么占用更多的空间以减少运算时间,要么使用更多的运算时间减少空间的占用。优秀的算法就是根据实际应用情况在这两者之间找一个平衡点。SLAB虽然能更快的分配内核对象,但是metadata,诸如缓存队列等复杂层次结构占用了大量的内存。
SLUB因此而诞生:
SLUB 不包含SLAB这么复杂的结构。SLAB不但有队列,而且每个SLAB开头保存了该SLAB对象的metadata。SLUB只将相近大小的对象对齐填入页面,并且保存了未分配的SLAB对象的链表,访问的时候容易快速定位,省去了队列遍历和头部metadata的偏移计算。该链表虽然和SLAB一样是每 CPU节点单独维护,但使用了一个独立的线程来维护全局的SLAB对象,一个CPU不使用的对象会被放到全局的partial队列,供其他CPU使用,平衡了个节点的SLAB对象。回收页面时,SLUB的SLAB对象是全局失效的,不会引起对象共享问题。另外,SLUB采用了合并相似SLAB对象的方法,进一步减少内存的占用。
据内核开发人员称,SLUB相对于SLAB有5%-10%的性能提升和减少50%的内存占用(是内核对象缓存占用的,不是全局哦,否则Linux Kernel可以修改主版本号了)。所以SLUB是一个时间和空间上均有改善的算法,而且SLUB完全兼容SLAB的接口,所以内核其他模块不需要修改即可从SLUB的高性能中受益。SLUB在2.6.22内核中理所当然的替代了SLAB。
阅读(923) | 评论(0) | 转发(0) |