Linux 内存管理:slab、伙伴系统与内存碎片问题详解

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

一、为什么 Linux 需要多种内存分配器

理解 slab 和伙伴系统的前提,是先理解一个基本矛盾:内核需要的内存块大小差异极大

  • 有些场景需要连续的大块物理内存,比如 DMA 缓冲区、大页映射,可能是几 MB 甚至几十 MB。
  • 有些场景只需要几十字节的小对象,比如 task_structinodefile 结构体,而且这些对象会被频繁地创建和销毁。

如果只用一种分配器来同时满足这两类需求,要么浪费严重,要么性能极差。因此 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、inodedentry)如果每次都用伙伴系统分配整页,会造成巨大的内部碎片和性能开销。slab 分配器的核心思路是:从伙伴系统申请整页,然后在页内切分成多个相同大小的小对象,并把这些对象缓存起来复用

工作原理

  1. 缓存(cache):每种内核对象类型对应一个 slab 缓存,比如 task_struct 缓存、inode 缓存。
  2. slab:一个 slab 由一个或多个连续页框组成,内部被切分成若干等大小的对象槽位。
  3. 三种状态:slab 分为 full(全部已分配)、partial(部分已分配)、free(全部空闲)。分配时优先从 partial 中取,释放时对象归还到 slab,slab 在三种状态间迁移。

slab 的优势

  • 消除内部碎片:对象大小与 slab 槽位精确匹配。
  • 提升性能:对象释放后不立即归还伙伴系统,而是留在缓存中,下次分配可直接复用,避免频繁的页分配和初始化。
  • 硬件缓存友好:同一类型的对象在内存中紧凑排列,有利于 CPU 缓存命中。

面试常见追问

问:slab、slub、slob 有什么区别?

答:三者是 slab 分配器的不同实现。slab 是早期实现,元数据复杂、开销较大;slub 是当前主流默认实现,简化了元数据,在大型系统上扩展性更好;slob 是面向嵌入式小内存系统的简化版本。面试中答出“slub 是默认实现”通常就够了。

四、内存碎片:问题的本质

内存碎片分为两类:

  • 内部碎片:分配出去的内存块比实际需求大,多余部分被浪费。比如伙伴系统分配 4KB 页但只需 100 字节。
  • 外部碎片:空闲内存总量足够,但被分割成不连续的小块,无法满足大块连续分配请求。

碎片是如何产生的

  1. 伙伴系统的 2 幂次限制:长期运行后,空闲页框分散在不同 order 的链表中,难以合并成大块。
  2. 小对象频繁分配释放:slab 虽然解决了小对象问题,但 slab 本身占用的页框如果长期不释放,也会加剧碎片。
  3. 不可移动页的钉住:内核页、DMA 缓冲区等无法迁移,阻碍了内存规整。

解决思路

Linux 内核提供了多种机制来对抗碎片:

  • 内存规整(Memory Compaction):将可移动页迁移到一侧,腾出连续空闲区域。这是 CONFIG_COMPACTION 的核心功能。
  • 页迁移(Page Migration):将页面内容复制到新位置,更新映射,从而整理出连续空间。
  • 反碎片化分组:将页框按可移动性分为不可移动、可回收、可移动三类,从源头减少碎片。
  • CMA(连续内存分配器):为 DMA 等需要大块连续内存的场景预留区域,平时可被可移动页使用,需要时再腾出。

五、面试答题框架建议

当面试官问“slab、伙伴系统与内存碎片”时,可以按以下逻辑组织回答:

  1. 先讲分层:伙伴系统管页框,slab 管小对象,二者是上下层关系。
  2. 再讲各自解决什么问题:伙伴系统解决大块连续分配,slab 解决小对象内部碎片和性能。
  3. 然后讲局限:伙伴系统有外部碎片,slab 有缓存膨胀问题。
  4. 最后讲整体方案:内存规整、页迁移、反碎片化分组等机制协同工作。

这样的回答既有层次感,又能体现你对 Linux 内存管理整体架构的理解,远比孤立地背诵定义更能打动面试官。

总结

维度 伙伴系统 slab 分配器
管理对象 物理页框 内核小对象
分配粒度 2^n 页 对象大小
主要解决 大块连续分配 内部碎片与性能
主要问题 外部碎片 缓存膨胀、回收延迟
关系 底层基础 构建于伙伴系统之上

理解这三者的协作与矛盾,不仅是通过面试的关键,也是深入 Linux 内核内存管理的第一步。

未经允许不得转载:任鹏个人博客 » Linux 内存管理:slab、伙伴系统与内存碎片问题详解

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏