1. 内存泄漏基础

1.1 什么是内存泄漏

内存泄漏(Memory Leak)是指程序在运行过程中动态分配的堆内存,由于程序设计错误导致在不再需要时无法被释放,从而造成系统可用内存持续减少的现象。在 Linux 内核开发中,内存泄漏通常表现为通过 kmalloc()、vmalloc()、alloc_pages() 等接口分配的内存块,其指针丢失或引用计数失衡,导致内存无法归还给系统。

核心定义: 内存泄漏 = 已分配堆内存 + 丢失释放路径 + 持续累积

以下是一个典型的内核内存泄漏代码示例:

#include <linux/slab.h>
#include <linux/module.h>

static int __init leak_init(void)
{
    char *buf;
    int i;

    for (i = 0; i < 100; i++) {
        buf = kmalloc(1024, GFP_KERNEL);  /* 每次分配 1KB */
        if (!buf)
            return -ENOMEM;
        /* 忘记调用 kfree(buf) —— 内存泄漏 */
    }
    return 0;
}

module_init(leak_init);

上述代码每次调用 kmalloc() 后,指针 buf 在下一轮循环中被覆盖,之前分配的内存块失去所有引用,永远无法释放。

内存泄漏生命周期 1. 内存分配 2. 正常使用 3. 正常释放 4. 指针丢失/覆盖 5. 内存孤岛 泄漏路径:分配 → 使用 → 指针丢失 → 内存孤岛(无法回收)

1.2 内存泄漏的危害

内存泄漏在 Linux 内核中属于严重缺陷,其危害随泄漏速率和系统运行时间呈指数级增长。以下是主要危害维度:

危害类型 具体表现 严重等级
系统性能下降 可用内存减少,触发频繁的页面回收和交换,系统响应变慢 中
内存耗尽(OOM) 系统无法分配新内存,触发 OOM Killer 杀死进程,甚至内核 panic 高
系统不稳定 关键内核模块因内存不足而异常,导致随机崩溃或死锁 高
资源浪费 泄漏的内存无法被其他进程或内核组件复用,降低硬件利用率 中
调试困难 泄漏通常需要长时间运行才能暴露,定位根因耗时巨大 极高
生产环境案例: 某网络设备驱动在热拔插场景下每次泄漏 4KB,以每分钟 100 次操作计算,24 小时后泄漏约 562MB,直接导致系统 OOM 重启。
内存泄漏累积危害效应 T0 T1 T2 T3 T4 (OOM) 正常 轻微 明显 严重 崩溃 泄漏量随时间线性增长,系统可用内存呈阶梯式下降,最终触发 OOM

1.3 常见内存泄漏场景概述

内核开发中,内存泄漏的根因通常集中在以下几个典型场景。理解这些场景有助于在代码审查和调试时快速定位问题。

1.3.1 分配后未释放

最直接的泄漏形式:调用 kmalloc()、kzalloc()、vmalloc() 等分配函数后,在函数返回或错误处理路径中遗漏对应的 kfree()、vfree() 调用。

1.3.2 引用计数失衡

内核对象(如 struct kobject、文件、网络套接字)通过引用计数管理生命周期。当 kobject_get() 与 kobject_put() 不匹配时,对象无法被释放,形成泄漏。

1.3.3 链表/哈希表节点未清理

将动态分配的内存节点插入链表或哈希表后,在删除节点时只执行了 list_del() 而未释放节点内存,导致节点成为泄漏点。

1.3.4 错误处理路径遗漏

在复杂的错误处理逻辑中,部分分支遗漏了已分配资源的释放。这是内核模块中最常见的泄漏来源。

1.3.5 缓存/池化对象泄漏

使用内存池(mempool)或 slab 缓存时,对象借出后未归还,或缓存清理函数未正确释放所有对象。

常见内存泄漏场景分类 内存泄漏 分配未释放 kmalloc/kfree 引用计数失衡 get/put 不匹配 链表节点泄漏 list_del 未释放 错误路径遗漏 goto 清理缺失 缓存/池泄漏 mempool/slab 排查建议 优先检查错误处理路径 → 核对引用计数配对 → 审查链表/哈希表清理逻辑 使用 kmemleak / KASAN / slub_debug 等工具辅助验证

场景对比速查表

场景 典型 API 检测难度 常见于
分配未释放 kmalloc / kfree 低 驱动 probe/remove 函数
引用计数失衡 kobject_get / kobject_put 中 设备模型、文件系统
链表节点泄漏 list_add / list_del 中 网络协议栈、缓存管理
错误路径遗漏 多分支 goto 清理 高 复杂初始化函数
缓存/池泄漏 mempool_alloc / mempool_free 高 块设备层、DMA 缓冲区