首先说一下写这篇博文的初衷:我对linux的学习,是不是都过度的专注于各个模块的实现,而忘记了其背后的设计初衷呢?
以下观点纯属作者个人观点,如果有写的不对的地方请指出。
伙伴算法的初衷: 为了兼顾内存分配效率以及减少外部碎片而设计出来的一种分配算法。
百度百科对伙伴算法的定义:计算机算法的一种,是为了核心内存管理能够快速响应请求,尽可能地在提高内存利用率的同时减少内存碎片的一种算法。
slab算法的初衷: 为了不同大小的对象而设计的一个分配算法,并兼顾效率以及碎片化程度。
另外,SLUB分配器的出现是为了解决slab的不足,引入的一种解决方案。
一下slub介绍摘自百度百科:
SLAB 分配器多年以来一直位于 Linux 内核的内存管理部分的核心地带,内核黑客们一般不愿意主动去更改它的代码,因为它实在是非常复杂,而且在大多数情况下,它的工作完成的相当不错。但是,随着大规模多处理器系统和 NUMA系统的广泛应用,SLAB 分配器逐渐暴露出自身的严重不足:
1、较多复杂的队列管理。在 SLAB 分配器中存在众多的队列,例如针对处理器的本地对象缓存队列,slab 中空闲对象队列,每个 slab 处于一个特定状态的队列中,甚至缓冲区控制结构也处于一个队列之中。有效地管理这些不同的队列是一件费力且复杂的工作。
2、slab 管理数据和队列的存储开销比较大。每个 slab 需要一个 struct slab 数据结构和一个管理所有空闲对象的 kmem_bufctl_t(4 字节的无符号整数)的数组。当对象体积较少时,kmem_bufctl_t 数组将造成较大的开销(比如对象大小为32字节时,将浪费 1/8 的空间)。为了使得对象在硬件高速缓存中对齐和使用着色策略,还必须浪费额外的内存。同时,缓冲区针对节点和处理器的队列也会浪费不少内存。测试表明在一个 1000 节点/处理器的大规模 NUMA 系统中,数 GB 内存被用来维护队列和对象的引用。
3、缓冲区内存回收比较复杂。
4、对 NUMA 的支持非常复杂。SLAB 对 NUMA 的支持基于物理页框分配器,无法细粒度地使用对象,因此不能保证处理器级缓存的对象来自同一节点。
5、冗余的 Partial 队列。SLAB 分配器针对每个节点都有一个 Partial 队列,随着时间流逝,将有大量的 Partial slab 产生,不利于内存的合理使用。
6、性能调优比较困难。针对每个 slab 可以调整的参数比较复杂,而且分配处理器本地缓存时,不得不使用自旋锁。
7、调试功能比较难于使用。
为了解决以上 SLAB 分配器的不足之处,内核开发人员 Christoph Lameter 在 Linux 内核 2.6.22 版本中引入一种新的解决方案:SLUB 分配器。SLUB 分配器特点是简化设计理念,同时保留 SLAB 分配器的基本思想:每个缓冲区由多个小的 slab 组成,每个 slab 包含固定数目的对象。SLUB 分配器简化了kmem_cache,slab 等相关的管理数据结构,摒弃了SLAB 分配器中众多的队列概念,并针对多处理器、NUMA 系统进行优化,从而提高了性能和可扩展性并降低了内存的浪费。为了保证内核其它模块能够无缝迁移到 SLUB 分配器,SLUB 还保留了原有 SLAB 分配器所有的接口 API 函数。
vmalloc:一般是为了获得大块内存时使用,主要是为了克服kmalloc或者alloc_page分配的内存物理上必须连续的苛刻条件;此时只要我有连续的虚拟内存,则可以忽略我可能没有连续的物理的事实
页面迁移:页面迁移涉及的初衷是在numa系统中提供把一些页面从一个内存节点迁移到另一个内存节点的能力。
TODO:cma