Linux 内核内存管理:Slab 分配器原理与内存泄漏排查

一、Slab 分配器背景

Linux 内核需要频繁分配/释放小内存对象(如进程描述符、文件对象)。传统伙伴系统(Buddy System)以页(通常 4KB)为单位分配,会产生内部碎片。Slab 分配器通过对象缓存机制解决该问题,其核心思想是:

  • 预分配内存块(slab)
  • 将内存块划分为固定大小的对象
  • 重复利用已释放对象
二、Slab 分配器原理
1. 三级结构

$$ \text{kmem_cache} \rightarrow \text{slab} \rightarrow \text{object} $$

  • kmem_cache:核心控制结构,管理特定类型对象(如 task_struct)
  • slab:物理内存页组成的容器
  • object:实际分配的最小单元
2. 状态管理

每个 slab 有三种状态:

  1. 满状态:所有对象已分配
  2. 空状态:所有对象空闲
  3. 部分状态:部分对象分配中

状态转换通过双向链表实现,满足分配请求时优先使用部分状态 slab。

3. 分配算法

当请求分配对象时:

// 伪代码流程
if (部分状态 slab 存在) 
    从其获取空闲对象
else if (空状态 slab 存在) 
    升级为部分状态并分配
else 
    创建新 slab

4. 着色机制

为减少 CPU 缓存冲突,Slab 在对象间插入颜色偏移(color offset): $$ \text{实际地址} = \text{基地址} + \text{颜色值} \times \text{缓存行大小} $$ 通过分散对象起始位置,提升缓存利用率。

三、内存泄漏排查

当内核对象未正确释放时,会导致 slab 缓存持续增长,引发内存泄漏。

排查工具
  1. /proc/slabinfo
    查看所有 slab 缓存状态:

    awk '{print $1,$2,$3,$4}' /proc/slabinfo | sort -k2 -nr
    

    关注 active_objs(活跃对象)与 num_objs(总对象)比值异常缓存。

  2. kmemleak
    内核内置检测工具,启用后扫描未引用内存:

    echo scan > /sys/kernel/debug/kmemleak  # 触发扫描
    cat /sys/kernel/debug/kmemleak          # 查看泄漏点
    

  3. slabtop
    实时监控 slab 使用:

    slabtop -s c  # 按缓存大小排序
    

    持续观察 ACTIVE 列增长情况。

排查步骤
  1. 定位异常缓存
    通过 /proc/slabinfo 找到对象数量持续增长的缓存名(如 dentry)。

  2. 分析分配路径
    使用 ftrace 跟踪对象分配:

    echo 1 > /sys/kernel/debug/tracing/events/kmem/kmalloc/enable
    cat /sys/kernel/debug/tracing/trace_pipe
    

  3. 检查引用链
    对于已知泄漏对象,通过 crash 工具分析引用关系:

    crash -s vmlinux vmcore
    > kmem -s kmem_cache_name
    

四、优化建议
  1. 定期监控 /proc/meminfo 中的 Slab 字段
  2. 为高频小对象设置专用缓存(kmem_cache_create()
  3. 在驱动模块卸载时强制回收缓存(kmem_cache_destroy()

通过理解 Slab 的对象复用机制状态管理策略,结合内核提供的诊断工具,可有效定位内存泄漏根源。实际应用中需注意:频繁创建/销毁缓存本身也会消耗资源,应保持缓存生命周期与业务需求匹配。

Logo

更多推荐