当前位置: 首页 > news >正文

Linux 内核 Slab 分配器核心组件详解

Slab 分配器是 Linux 内核中用于高效管理内存的机制,其核心目标是通过对象缓存减少内存碎片和分配/释放开销。以下详细解析其核心组件及其协作关系:

 

一、Slab 系统的核心组件

 

组件 描述 作用场景

Slab 描述符 每个 Slab 的管理结构(如 struct slab),记录页帧状态、空闲对象链表等。 跟踪 Slab 的元数据(如对象数量、空闲链表头、所属缓存等)。

Slab 节点 NUMA 架构下的内存节点(struct kmem_cache_node),管理本地 Slab 资源。 优化 NUMA 系统内存访问,减少跨节点访问延迟。

本地对象缓冲池 每 CPU 的缓存(struct kmem_cache_cpu),存放快速分配的空闲对象。 无锁快速分配对象,避免全局竞争。

共享对象缓冲池 节点级别的共享缓存池(struct kmem_cache_node 中的链表)。 当本地缓冲池耗尽时,从共享池批量获取对象或 Slab。

三个 Slab 链表 每个节点维护三个链表:slabs_full、slabs_partial、slabs_free。 按 Slab 的填充状态分类,优先从 slabs_partial 分配,释放时调整链表位置。

N 个 Slab 物理连续的页帧集合,划分为多个等大小的对象。 承载实际对象存储,一个 Slab 可分配多个对象。

Slab 缓存对象 每个 Slab 中划分出的内存块,用于存储特定类型的内核对象(如 inode)。 直接被内核代码分配和释放,避免频繁调用页分配器。

 

二、组件协作流程

 

1. 对象分配流程

 

步骤 1:尝试从 本地对象缓冲池(Per-CPU 缓存)获取空闲对象。

步骤 2:若本地池为空,从 共享对象缓冲池(节点 slabs_partial 链表)批量获取对象填充本地池。

步骤 3:若共享池耗尽,从 Slab 空闲链表(slabs_free)分配新 Slab,分割为对象后加入本地池。

步骤 4:若系统内存不足,触发页分配器(如 Buddy System)分配新页帧创建 Slab。

 

2. 对象释放流程

 

步骤 1:将对象归还到 本地对象缓冲池。

步骤 2:若本地池已满,将部分对象批量返还到 共享对象缓冲池(slabs_partial)。

步骤 3:若 Slab 中所有对象均释放,将 Slab 移至 slabs_free 链表,在内存紧张时释放回页分配器。

三、关键数据结构关系

 

struct kmem_cache

├── struct kmem_cache_cpu *cpu_slab // 每 CPU 的本地缓存

├── struct kmem_cache_node *node[NUM_NODES] // NUMA 节点共享缓存

│ ├── struct list_head slabs_full // 满 Slab 链表

│ ├── struct list_head slabs_partial // 部分满 Slab 链表

│ └── struct list_head slabs_free // 空闲 Slab 链表

└── unsigned int object_size // 对象大小

 

struct slab

├── struct list_head slab_list // 所属链表(full/partial/free)

├── void *freelist // 空闲对象链表头

├── unsigned int active_objects // 已分配对象数

└── unsigned int num_pages // 占用的页帧数

 

四、代码案例分析:Slab 分配器的内部操作

 

以下伪代码展示 Slab 分配器在对象分配时的核心逻辑:

 

// 分配对象

void *kmem_cache_alloc(struct kmem_cache *cache, gfp_t flags) {

    struct kmem_cache_cpu *cpu_slab = get_cpu_ptr(cache->cpu_slab);

    void *object;

 

    // 1. 尝试从本地缓冲池获取对象

    if (likely(cpu_slab->freelist)) {

        object = cpu_slab->freelist;

        cpu_slab->freelist = get_next_freepointer(object);

        return object;

    }

 

    // 2. 本地池为空,从节点共享池填充

    if (unlikely(!fill_local_cache(cpu_slab, cache, flags))) {

        // 3. 共享池不足,分配新 Slab

        struct slab *new_slab = allocate_slab(cache, flags);

        if (!new_slab)

            return NULL; // 内存不足

        cpu_slab->freelist = new_slab->freelist;

        cpu_slab->slab = new_slab;

        object = cpu_slab->freelist;

        cpu_slab->freelist = get_next_freepointer(object);

        return object;

    }

    // ... 其他错误处理

}

 

// 填充本地缓冲池

static int fill_local_cache(struct kmem_cache_cpu *cpu_slab, 

                           struct kmem_cache *cache, 

                           gfp_t flags) {

    struct kmem_cache_node *node = cache->node[cpu_to_node(smp_processor_id())];

    struct slab *slab;

 

    // 从节点的部分满链表获取 Slab

    spin_lock(&node->list_lock);

    slab = list_first_entry_or_null(&node->slabs_partial, struct slab, slab_list);

    if (!slab) {

        // 若无部分满 Slab,尝试从空闲链表分配

        slab = list_first_entry_or_null(&node->slabs_free, struct slab, slab_list);

        if (!slab) {

            spin_unlock(&node->list_lock);

            return 0; // 需要分配新 Slab

        }

        list_move(&slab->slab_list, &node->slabs_partial);

    }

    // 将 Slab 的空闲对象导入本地缓存

    cpu_slab->freelist = slab->freelist;

    slab->freelist = NULL;

    slab->active_objects = cache->num_objects;

    list_move(&slab->slab_list, &node->slabs_full);

    spin_unlock(&node->list_lock);

    cpu_slab->slab = slab;

    return 1;

}

 

五、关键设计优势

 

减少内存碎片

Slab 按对象大小预分配内存块,避免频繁分割物理页,减少外部碎片。

 

提升分配速度

通过本地缓冲池(Per-CPU 无锁缓存)实现快速分配,避免全局锁竞争。

 

NUMA 优化

每个节点管理本地 Slab,减少跨节点内存访问带来的性能损耗。

 

内存重用

释放的对象归还到空闲链表,避免频繁调用页分配器,降低系统开销。

 

六、实际观察工具

 

查看 Slab 状态

 

cat /proc/slabinfo

输出示例:显示每个缓存的名称、对象数量、活动对象数、Slab 使用情况等。

内核调试工具

使用 systemtap 或 ftrace 跟踪特定缓存的对象分配/释放路径。

相关文章:

  • 基于51单片机和8X8点阵屏、独立按键的跳跃躲闪类小游戏
  • 如何在同一台电脑上安装并运行多个版本的 IntelliJ IDEA
  • xilinx的GT配置说明(一)
  • 【考研数学:高数6】一元函数微分学的应用(二)——中值定理、微分等式和微分不等式
  • AT2659低噪声放大器芯片
  • [KCTF]rev_babyrev
  • 永磁同步电机控制算法--抗饱和PI
  • C#winform画图代码记录
  • 基于地形数据计算山体阴影
  • YOLO-FireAD:通过混合注意力与双池化融合实现高精度实时火灾检测
  • 专题:2025中国游戏科技发展白皮书报告汇总解读|附130+份报告PDF汇总下载
  • Java中的设计模式:23种经典模式在实际项目中的应用案例
  • 行为设计模式之Observer(观察者)
  • 设计模式-组合模式
  • 复习日!!
  • 软件设计模式(Java)复习
  • 【Docker】快速入门与项目部署实战
  • [3-02-01].第03节:环境搭建 - 在Docker中安装部署Redis环境:
  • (十一)优化算法(Optimization):深度学习训练中的收敛性分析与泛化理论
  • Java锁机制对决:ReadWriteLock vs StampedLock
  • 做家政网上推广网站/品牌推广方式都有哪些
  • 做设计英文网站/如何联系百度人工客服电话
  • 网站备案照相/网络上市场推广
  • 赣州市网站建设公司/百度客服在哪里找
  • 建设部网站怎么查询相关专业/公司官网怎么做
  • 沪浙网站/下载百度2023最新版