在 Linux 面试中,内存管理几乎是绕不开的核心话题。面试官常常会从一道看似简单的题目切入,比如“请解释一下 slab 和伙伴系统的区别”,然后层层追问,最终落到“内存碎片是怎么产生的、如何解决”这个终极问题上。本文将从面试回答的角度,系统梳理这三个概念及其内在联系。

一、为什么 Linux 需要多种内存分配器
理解 slab 和伙伴系统的前提,是先理解一个基本矛盾:内核需要的内存块大小差异极大。
- 有些场景需要连续的大块物理内存,比如 DMA 缓冲区、大页映射,可能是几 MB 甚至几十 MB。
- 有些场景只需要几十字节的小对象,比如
task_struct、inode、file结构体,而且这些对象会被频繁地创建和销毁。
如果只用一种分配器来同时满足这两类需求,要么浪费严重,要么性能极差。因此 Linux 采用了分层协作的设计:伙伴系统负责管理物理页框(以页为最小单位),slab 分配器则建立在伙伴系统之上,负责小对象的精细分配。
二、伙伴系统:物理页框的管理者
核心思想
伙伴系统(Buddy System)把物理内存按 2 的幂次划分为不同大小的块,每个块的大小是 2^n 个页框(通常一页为 4KB)。当需要分配内存时,系统会找到最接近需求大小的块;如果该块比需求大,就不断对半分裂,直到得到合适的大小。释放时,如果相邻的“伙伴”块也空闲,就合并成更大的块。
关键特点
- 分配粒度是页,最小 4KB,最大可以到几百 MB。
- 分裂与合并保证了空闲内存尽可能保持连续的大块。
- 分配和释放的时间复杂度为 O(log n),效率很高。
- 通过
free_area[]数组维护不同阶(order)的空闲链表,order 从 0 到 MAX_ORDER(通常为 10 或 11)。
面试常见追问
问:伙伴系统能解决内存碎片吗?
答:伙伴系统能缓解外部碎片,但无法彻底消除。因为它只能按 2 的幂次分配,如果系统频繁请求 3 页、5 页这样的非 2 幂次大小,就会产生大量无法合并的小块。更重要的是,伙伴系统不管理小对象,如果每个小对象都占用一整页,内部碎片会极其严重。
三、slab 分配器:小对象的高速缓存
为什么需要 slab
内核中大量的小对象(如 task_struct 约 1.7KB、inode、dentry)如果每次都用伙伴系统分配整页,会造成巨大的内部碎片和性能开销。slab 分配器的核心思路是:从伙伴系统申请整页,然后在页内切分成多个相同大小的小对象,并把这些对象缓存起来复用。
工作原理
- 缓存(cache):每种内核对象类型对应一个 slab 缓存,比如
task_struct缓存、inode缓存。 - slab:一个 slab 由一个或多个连续页框组成,内部被切分成若干等大小的对象槽位。
- 三种状态:slab 分为 full(全部已分配)、partial(部分已分配)、free(全部空闲)。分配时优先从 partial 中取,释放时对象归还到 slab,slab 在三种状态间迁移。
slab 的优势
- 消除内部碎片:对象大小与 slab 槽位精确匹配。
- 提升性能:对象释放后不立即归还伙伴系统,而是留在缓存中,下次分配可直接复用,避免频繁的页分配和初始化。
- 硬件缓存友好:同一类型的对象在内存中紧凑排列,有利于 CPU 缓存命中。
面试常见追问
问:slab、slub、slob 有什么区别?
答:三者是 slab 分配器的不同实现。slab 是早期实现,元数据复杂、开销较大;slub 是当前主流默认实现,简化了元数据,在大型系统上扩展性更好;slob 是面向嵌入式小内存系统的简化版本。面试中答出“slub 是默认实现”通常就够了。
四、内存碎片:问题的本质
内存碎片分为两类:
- 内部碎片:分配出去的内存块比实际需求大,多余部分被浪费。比如伙伴系统分配 4KB 页但只需 100 字节。
- 外部碎片:空闲内存总量足够,但被分割成不连续的小块,无法满足大块连续分配请求。
碎片是如何产生的
- 伙伴系统的 2 幂次限制:长期运行后,空闲页框分散在不同 order 的链表中,难以合并成大块。
- 小对象频繁分配释放:slab 虽然解决了小对象问题,但 slab 本身占用的页框如果长期不释放,也会加剧碎片。
- 不可移动页的钉住:内核页、DMA 缓冲区等无法迁移,阻碍了内存规整。
解决思路
Linux 内核提供了多种机制来对抗碎片:
- 内存规整(Memory Compaction):将可移动页迁移到一侧,腾出连续空闲区域。这是
CONFIG_COMPACTION的核心功能。 - 页迁移(Page Migration):将页面内容复制到新位置,更新映射,从而整理出连续空间。
- 反碎片化分组:将页框按可移动性分为不可移动、可回收、可移动三类,从源头减少碎片。
- CMA(连续内存分配器):为 DMA 等需要大块连续内存的场景预留区域,平时可被可移动页使用,需要时再腾出。
五、面试答题框架建议
当面试官问“slab、伙伴系统与内存碎片”时,可以按以下逻辑组织回答:
- 先讲分层:伙伴系统管页框,slab 管小对象,二者是上下层关系。
- 再讲各自解决什么问题:伙伴系统解决大块连续分配,slab 解决小对象内部碎片和性能。
- 然后讲局限:伙伴系统有外部碎片,slab 有缓存膨胀问题。
- 最后讲整体方案:内存规整、页迁移、反碎片化分组等机制协同工作。
这样的回答既有层次感,又能体现你对 Linux 内存管理整体架构的理解,远比孤立地背诵定义更能打动面试官。
总结
| 维度 | 伙伴系统 | slab 分配器 |
|---|---|---|
| 管理对象 | 物理页框 | 内核小对象 |
| 分配粒度 | 2^n 页 | 对象大小 |
| 主要解决 | 大块连续分配 | 内部碎片与性能 |
| 主要问题 | 外部碎片 | 缓存膨胀、回收延迟 |
| 关系 | 底层基础 | 构建于伙伴系统之上 |
理解这三者的协作与矛盾,不仅是通过面试的关键,也是深入 Linux 内核内存管理的第一步。
未经允许不得转载:任鹏个人博客 » Linux 内存管理:slab、伙伴系统与内存碎片问题详解

