少于 1 分钟阅读 次阅读

在 Linux 内核中,内存管理是操作系统最基础也最关键的组成部分之一。内核需要频繁地创建和销毁各种数据结构(如进程描述符、文件索引节点等),如果每次分配和释放都直接调用底层页分配器,不仅效率低下,还会产生严重的内存碎片。为了解决这些问题,Linux 引入了slab 分配器(slab allocator),它是一种基于对象缓存的内存管理机制,专门用于高效管理内核中频繁使用的小对象。

核心概念一句话定义

  1. slab 层:Linux 内核中用于缓存频繁分配/释放的数据结构对象的内存管理层,旨在减少内存碎片和提高分配效率。
  2. 高速缓存:slab 层中用于存储特定类型对象(如task_structinode)的容器,每个对象类型对应一个独立的高速缓存。
  3. slab:组成高速缓存的基本单元,由一个或多个连续物理页构成,内部划分为多个等大的对象存储槽。
  4. slab 状态:根据对象占用情况,slab 可分为“满”(所有对象已分配)、“部分满”(部分对象分配)和“空”(所有对象空闲)三种状态。
  5. 对象着色:通过偏移 slab 内对象的起始地址,避免不同 slab 中对象映射到同一 CPU 高速缓存行,减少伪共享冲突。
  6. NUMA 感知:slab 分配器可识别非一致性内存访问架构,优先从请求线程所在的 CPU 节点分配内存,提升访问性能。

设计原理与工作机制

1. 为何需要 slab 层?

内核中常通过空闲链表缓存常用对象:分配时直接从链表取用,释放时归还链表而非真正释放内存。然而,这种分散的空闲链表存在全局管控难题——内存紧缺时,内核无法主动通知各链表释放内存。slab 层作为全局性的缓存管理器,统一协调所有对象的缓存分配与回收,确保内存紧张时能系统性释放缓存。

2. 三层结构:高速缓存 → slab → 对象

slab 层采用层次化设计:

  • 高速缓存:每个内核数据结构类型(如struct inode)对应一个独立高速缓存,通过kmem_cache结构体管理。
  • slab:高速缓存被划分为多个 slab,每个 slab 占据连续物理页(通常为一页),内部划分为多个等大的对象存储槽。
  • 对象:被缓存的实际数据结构实例,存储在 slab 的槽位中。

alt text 这种设计使得内存块按对象大小整齐划分,避免外部碎片;同时通过状态链表(满/部分满/空)快速定位可用空间。

3. 分配与回收流程

当内核请求新对象时(如需要新的inode结构):

  1. 优先从部分满 slab分配(避免频繁创建新 slab)。
  2. 若无部分满 slab,则从空 slab分配。
  3. 若无空 slab,才调用kmem_getpages()申请新内存页创建 slab。

释放对象时,对象被标记为空闲并返回对应 slab。仅当内存不足或高速缓存被销毁时,才调用kmem_freepages()释放物理页,最大限度减少底层页分配器的调用次数。

4. 性能优化策略

  • 每 CPU 缓存:每个 CPU 拥有独立的对象缓存,分配/释放时可避免锁竞争。
  • 着色机制:通过计算不同的偏移量,使同一高速缓存内不同 slab 的对象起始地址错开,防止多个对象占用同一 CPU 缓存行,提升并发效率。
  • NUMA 优化:在 NUMA 系统中,优先从本地内存节点分配,减少远程访问延迟。

slab 层的优势与意义

  1. 提升性能:通过缓存常用对象,避免频繁的页分配与初始化开销。
  2. 减少碎片:对象按固定大小在 slab 内连续存放,释放后空间可立即重用,有效抑制内存碎片。
  3. 全局可控:内核可统一监控所有高速缓存,在内存紧张时按需回收空闲 slab。
  4. 透明管理:内核模块仅需调用简单接口(如kmem_cache_createkmalloc),复杂状态维护由 slab 层内部完成。

留下评论