内存管理四 内存泄漏检测kmemleak

本文介绍Kmemleak作为内核泄漏检测工具的工作原理及使用方法,并通过实例展示如何利用它来定位和解决内核内存泄漏问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简介:

    Kmemleak工作于内核态,Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中,Kmemcheck能够帮助定位大多数内存错误的上下文。

二、使用方法:

(1)打开方法:

    在kernel的defconfig的文件中,使能如下宏控

    CONFIG_SLUB_DEBUG=y
    CONFIG_DEBUG_KMEMLEAK=y
    CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=n
    CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=40000

    其中kmemleak缓冲区的大小通过配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE设置

CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=40000  // 可以把这个值稍微改大一点,其中Kmemleak相关的代码在如下路径的文件中: kernel-4.9/drivers/misc/mediatek/mem/mtk_memcfg.c

    当发现一些SLUB泄漏(通过slub机制分配的内存,但未释放)的案例时,需要同时添加slabtrace的节点,以便获取到通过SLUB机制释放的内存的堆栈信息(/proc/mtk_memcfg/slabtrace)。下面会通过案例解释。

(2)检测方法(假设有泄漏):

a、一次scan: echo scan > sys/kernel/debug/kmemleak//开始扫描
     然后 cat sys/kernel/debug/kmemleak 会得到很多backtrace,但是这其中有些是误抓的(kmemleak存在误报情况)
b、然后echo clear > sys/kernel/debug/kmemleak 清除log
    第二次scan:echo scan > sys/kernel/debug/kmemleak//开始扫描
    过段时间等待leak的积累, 然后 cat sys/kernel/debug/kmemleak
    很多第一次误报的backtrace没有了,会得到很多重复的backtrace,假设这样的backtrace称为A;
c、kmemleak的特征是A backtrace会越来越多,不断增长,而且这里就是泄漏的点;

 

二、实验:

    通过修改linux_DEVICE_ATTR创建设备节点程序一文中的show函数如下:

static ssize_t hello_test_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    size_t kmalloc_size = 26;
    
    char *hello_test = kmalloc(kmalloc_size,GFP_KERNEL);
    
    hello_test[kmalloc_size - 1] = 'x';
    
    return 0;
}

    以上函数可以看出每次cat对应的节点都会通过SLUB申请26字节的大小的内存,但并没有去释放(free)这一块内存,所以可以通过如下方式,不停的去cat此节点,就可以看出对应的kmemleak中频繁出现此函数:

    cd  /sys/bus/platform/drivers/hello_test/odm:hello_test     //进入到对应的路径

    while true;do cat hello_test;done;                                      //不停的去cat对应的节点

    此时去进行二中的检测方法:

           echo clear > sys/kernel/debug/kmemleak
           echo scan > sys/kernel/debug/kmemleak
           cat sys/kernel/debug/kmemleak

    可以发现如下堆栈信息:

unreferenced object 0xffffffc7e779d800 (size 64):
  comm "cat", pid 17097, jiffies 4295075509 (age 564.640s)
  hex dump (first 32 bytes):
    6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
    6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
  backtrace:
    [<ffffff94f34a439c>] create_object+0x1cc/0x478
    [<ffffff94f43d3fc8>] kmemleak_alloc+0x60/0xd8
    [<ffffff94f349a5f4>] kmem_cache_alloc_trace+0x274/0x2d8
    [<ffffff94f3dc435c>] hello_test_show+0x2c/0x38
    [<ffffff94f382c2a0>] dev_attr_show+0x40/0x88
    [<ffffff94f3565230>] sysfs_kf_seq_show+0x128/0x1c8
    [<ffffff94f3562f98>] kernfs_seq_show+0x80/0x98
    [<ffffff94f34e7184>] seq_read+0x14c/0x720
    [<ffffff94f3563fec>] kernfs_fop_read+0x1e4/0x280
    [<ffffff94f34b070c>] __vfs_read+0x74/0x1d0
    [<ffffff94f34b1a80>] vfs_read+0x98/0x188
    [<ffffff94f34b3570>] SyS_read+0x68/0xe0
    [<ffffff94f3282f70>] el0_svc_naked+0x24/0x28
    [<ffffffffffffffff>] 0xffffffffffffffff

    另外,kmalloc函数是基于SLUB机制释放的内存,可以通过上述的的slabtrace的节点看到对应的的内存大小再不断的增加:

======= kmem_cache: kmalloc-64 alloc_calls =======
  19537 selinux_cred_prepare+0x34/0x68 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349e048
  50318 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
  13326 load_elf_binary+0x30c/0x1760 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349aa20

   测试方法是:

         在一个cmd终端不停的执行:/sys/bus/platform/drivers/hello_test/odm:hello_test # while true;do cat hello_test;done;

         在另外一个终端不停的执行:/proc/mtk_memcfg # while true;do cat slabtrace|grep -nr hello;done;

   可以看到如下结果:

(standard input):45:  53439 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53401 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53351 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53370 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53368 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
......
(standard input):45:  53461 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53491 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630

//会发现53439 - 53491 的数值会不断的增加,上面只截取了一部分

二、总结:

    kmemleak的问题比较难以排查,需要复现找到规律,看具体那一块,或者哪一个函数存在内存的泄漏,这样才能具体的去定位解决问题。

 

作者:frank_zyp 
您的支持是对博主最大的鼓励,感谢您的认真阅读。 
本文无所谓版权,欢迎转载。

<think>好的,用户想了解Linux的内存管理机制或相关问题的解决方案。首先,我需要回忆一下之前提供的引用内容,确保回答准确。引用中提到了SLAB机制、缓存与缓冲区的优化、内存泄漏检测,以及内存分配释放的实现。这些内容可以作为回答的基础。 用户可能需要一个结构化的教程,所以应该分点讲解核心机制,比如物理内存管理、虚拟内存、SLAB/SLUB分配器、页面回收等。同时,结合引用中的例子,比如SLAB的工作流程,可以详细说明。 接下来是常见问题与解决方案。比如内存不足、内存泄漏、缓存占用过高。这里需要引用提到的调整内核参数、检测工具如Valgrind、调整vm.swappiness等方法。要确保每个问题都有对应的解决策略,并引用相关引用来源。 最后,生成相关问题是必要的。根据用户的问题,可能涉及内存分配原理、优化方法、工具使用等。需要确保问题覆盖教程中的重点内容,帮助用户深入理解。 需要注意使用正确的中文表达,避免技术术语错误,同时按照系统指令使用正确的LaTeX格式。例如,在提到数学公式或算法时,用$...$包裹。例如,内存分配算法中的计算可以用$O(1)$复杂度来表示。不过当前问题可能不需要太多数学公式,但如果有相关部分需要注意格式。 检查引用标记是否正确添加,比如提到SLAB时引用[^4],调整参数时引用[^2]等。确保每个解决方案都有对应的引用支持,增强可信度。 还要考虑用户可能的实际需求,比如系统管理员遇到内存不足的问题,开发者处理内存泄漏,或者学习者理解机制。因此,回答需要兼顾理论和实践,提供具体的命令和工具示例,如free、top、Valgrind等。 最后,保持回答结构清晰,分部分讲解机制和解决方案,使用代码块展示命令,帮助用户直接应用。同时,相关问题要引导用户进一步探索,覆盖教程中的各个知识点。</think>### Linux内存管理机制解析与常见问题解决方案 #### 一、Linux内存管理核心机制 1. **物理内存管理** Linux通过分页机制管理物理内存,最小单位为$4\text{KB}$的页帧(Page Frame)。使用**伙伴系统(Buddy System)**实现大块内存分配与碎片整理[^4],例如分配$2^n$页时通过拆分/合并相邻空闲块实现高效管理。 2. **虚拟内存机制** 通过页表映射实现虚拟地址到物理地址的转换,支持内存保护与共享。关键数据结构包括: ```c struct mm_struct { // 进程内存描述符 unsigned long start_code, end_code; // 代码段范围 pgd_t *pgd; // 页全局目录指针 }; ``` 3. **SLAB/SLUB分配器** 针对内核对象(如task_struct)的高频分配场景,采用预分配+对象缓存机制。例如创建新slab时: ```bash # 查看系统slab缓存 cat /proc/slabinfo ``` 其时间复杂度为$O(1)$,显著优于通用分配器[^1][^4]。 4. **页面回收机制** 使用LRU算法配合**kswapd**守护进程回收内存,通过`/proc/sys/vm/swappiness`调整交换倾向(默认值60)[^2]。 #### 二、典型问题与解决方案 1. **内存不足(OOM)** **现象**:系统日志出现`Out of Memory: Kill process` **排查步骤**: - 使用`free -h`查看内存分布 - 分析`/var/log/kern.log`定位触发进程 - 调整OOM Killer策略: ```bash echo -17 > /proc/[PID]/oom_adj # 保护关键进程 ``` 2. **内存泄漏检测** **用户空间工具**:Valgrind Massif ```bash valgrind --tool=massif ./your_program ms_print massif.out.[pid] # 生成内存分配曲线 ``` **内核空间检测**: - 启用`kmemleak`内核配置 - 查看泄漏报告: ```bash cat /sys/kernel/debug/kmemleak ``` 3. **缓存/缓冲区占用过高** **优化策略**: - 调整脏页刷新阈值(默认20%): ```bash sysctl -w vm.dirty_ratio=10 # 立即生效 ``` - 限制文件系统缓存: ```bash mount -o remount,noatime,nodiratime /dev/sda1 # 示例调整 ``` #### 三、进阶操作示例 1. **手动触发内存回收** ```bash sync # 先同步数据 echo 3 > /proc/sys/vm/drop_caches # 清空pagecache/slab ``` 2. **分析内存碎片** ```bash cat /proc/buddyinfo # 查看伙伴系统碎片情况 cat /proc/pagetypeinfo # 详细页类型分布 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值