引言

内存管理是 Linux 内核最为核心的子系统之一。在操作系统运行过程中,内核需要频繁地分配和释放大量小对象(如 task_struct、inode、dentry 等),这些对象的大小通常只有几百字节,远小于内存页大小(4KB)。如果直接基于页分配器(Buddy System)来管理这些小对象,必然会导致严重的内部碎片问题。

为了解决这个问题,Linux 内核引入了 Slab 分配器——一种专门用于管理小对象内存的高效分配机制。Slab 分配器不仅减少了内存碎片,还通过对象复用、CPU 缓存着色、per-CPU 缓存等手段,极大地提升了内存分配和释放的性能。

本文将从 Slab 分配器的设计动机出发,深入剖析其内部数据结构、工作原理、实现细节以及在实际系统调优中的应用。

1. 从 Buddy System 到 Slab:问题与设计动机

1.1 页级分配器的局限性

Linux 内存管理的最高层是页分配器(Buddy System),它负责管理物理页框(通常 4KB),分配和释放的基本单位是页。Buddy System 解决了外部碎片问题,但它至少分配一个完整页面。当一个内核对象只有 200 字节却占用 4096 字节时,将近 95% 的内存被浪费——这就是内部碎片。

1.2 小对象分配的痛点

内核运行中频繁创建/销毁的对象:

  • task_struct(进程描述符):约 7KB,但在 fork 时频繁分配
  • inode 和 dentry(文件系统缓存):几百字节
  • file 结构体:打开文件时分配
  • 网络相关的 sk_buff:每个数据包都需要
  • 各种驱动私有数据结构

除了内存浪费,直接基于页分配还有两个性能问题:

  1. 初始化开销:每个对象都涉及复杂的初始化工作(如锁、链表头),频繁分配/释放带来巨大开销
  2. 缓存抖动:反复分配释放导致 CPU 缓存频繁失效,严重影响性能

1.3 Slab 分配器的设计目标

Slab 分配器的核心设计目标可以归纳为:

  • 减少内部碎片:精确按对象大小分配,几乎零浪费
  • 对象复用:释放的对象回到缓存中,避免反复构造/析构
  • 硬件缓存友好:通过缓存着色(Cache Coloring)减少 Cache 冲突
  • per-CPU 缓存:每个 CPU 有独立缓存,避免锁竞争
  • NUMA 感知:支持按节点分配,减少远端内存访问

2. Slab 分配器的三层架构

Linux 内核中的小型对象内存分配采用了一个三层架构:

 ------------------------------------------------------- 
|                   用户接口层                           |
|   kmalloc() / kfree() / kmem_cache_alloc() / free()    |
 ------------------------------------------------------- 
                         ↓
 ------------------------------------------------------- 
|              Slab 分配器(通用层)                      |
|   kmem_cache 管理对象缓存池(slabs)                    |
 ------------------------------------------------------- 
                         ↓
 ------------------------------------------------------- 
|              页分配器(Buddy System)                   |
|             伙伴系统分配物理页                           |
 ------------------------------------------------------- 

2.1 顶层:kmalloc — 通用内存分配

kmalloc() 是最常用的内核内存分配接口,它类似于用户态的 malloc()。kmalloc 通过预定义的一组不同大小的 kmem_cache(称为 "size cache")来分配内存:

// Linux 内核中 kmalloc 的实现(简化版)
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags)
{
    struct kmem_cache *cachep;
    void *ret;

    if (size                        
                    
点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部