Linux内存泄漏排查经验案例

内存泄漏可以使用kmemleak等工具排查,但我的项目使用该工具无法查到原因。在跑Linux休眠唤醒的压力测试过程中发现,meminfo里的free内存一直在减小,SUnreclaim内存一直在增大,即系统发生了内存泄露。该压力测试只测试系统的休眠唤醒稳定性,基本就是各个驱动模块的suspend和resume函数在反复调用,因此猜测极大概率是某个驱动的suspend或者resume函数里存在内存泄露。我的测试脚本非常简单,如下所示:

cp /mnt/nfs/lib/libpmu.so /lib

#加载pmu驱动
modprobe pmu

#循环打印meminfo和slabinfo
(
while :
do
	echo 3 > /proc/sys/vm/drop_caches
    cat /proc/meminfo
	cat /proc/slabinfo
	sync
    sleep 1
done
)&

#循环执行休眠唤醒测试999999次
cd /mnt/nfs/app/pmu_sample && ./pmu_sample -p cfg/pmu_rtc_sleep.cfg -r 999999

以下是内存泄露的情况,系统运行20多分钟泄露了148KB的内存,运行一晚上会泄露8MB的内存。可以看到主要是Slab中的SUnreclaim内存在泄露。

既然是slab内存泄露,通过cat /proc/slabinfo就可以更具体的信息,前后对比如下:

从前后对比的slabinfo来看,是slab中的kmalloc-64内存池在不停的泄露。由于我的设备运行环境是休眠唤醒压力测试,没有什么业务在运行,因此理论上系统开始休眠唤醒之后内存申请和释放的地方是极少的,这种情况下可以考虑直接用dump_stack这个利器去查找泄露的地方。可以从两个地方入手,一是kmalloc函数,可以在判断出是申请kmalloc-64时进行dump_stack;二是找到/proc/slabinfo实现的地方,并进一步找到让slabinfo->num_objs成员增加的地方。这里我选择了后者。

首先查看一下/proc/slabinfo的实现:

/proc/slabinfo实现源码
跟踪num_objs成员来源
来源于kmem_cache_node的total_objects成员

可以看到,cat /proc/slabinfo打印的值是来源于kmem_cache_node的total_objects成员,在内核中检索total_objects,如下所示,发现只有“atomic_long_add(objects, &n->total_objects)”。

 

 找到atomic_long_add调用的地方,在此处加上dump_stack。test_flag是我加的触发条件,避免在开机阶段就触发dump_stack,这个test_flag是在系统开始休眠唤醒操作时置1的,因此开机阶段不会打印。这里起始还可以通过判断s->name是否等于kmalloc-64来进一步过滤出kmalloc-64,但我的测试环境不会有很多内存申请和释放,所以干脆不加。

 加上之后再次进行压力测试,在打印的日志中搜索kmalloc-64,如下所示。可以看到kmalloc-64是一直在泄漏的,而且每次都是dump_stack之后增加,这说明我们增加的dump_stack正确捕捉到了泄漏的位置。

下面是dump_stack的打印:

dump_stack打印

从dump_stack的信息可知是clk模块申请了内存,在clk的resume函数中调用了clk_get,clk_get调用clk_hw_create_clk,clk_hw_create_clk函数会调用alloc_clk,这个函数实现如下。就是其中的kzalloc申请的内存没有释放。实际上clk_get有一个对应的函数clk_put,这个函数会释放对应的内存,而我们的clk驱动只调用了clk_get,没有调用clk_put,因此造成了内存泄漏。

static struct clk *alloc_clk(struct clk_core *core, const char *dev_id,
			     const char *con_id)
{
	struct clk *clk;

	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
	if (!clk)
		return ERR_PTR(-ENOMEM);

	clk->core = core;
	clk->dev_id = dev_id;
	clk->con_id = kstrdup_const(con_id, GFP_KERNEL);
	clk->max_rate = ULONG_MAX;

	return clk;
}

修改完毕后再次运行,可以看到,没有再出现泄漏了。

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux 内存泄漏排查的方法有很多种,这里介绍一些常用的方法: 1. 使用 Valgrind 工具:Valgrind 是一个强大的开源工具集,可以用来检测内存泄漏、越界访问、使用未初始化的内存等问题。使用 Valgrind 工具可以非常方便地发现内存泄漏问题,具体使用方法如下: ```bash valgrind --leak-check=full ./your_program ``` 这条命令会在运行你的程序时启动 Valgrind 工具,并检查程序中的内存泄漏问题。Valgrind 工具会输出详细的检查结果,包括内存泄漏的位置、大小等信息。 2. 使用 GNU glibc 内存泄漏检测:GNU glibc 是 Linux 系统中常用的 C/C++ 库,它提供了一些内存泄漏检测的函数。具体使用方法如下: ```c++ #include <mcheck.h> int main(int argc, char* argv[]) { mtrace(); // your code here muntrace(); return 0; } ``` 这段代码会在程序启动时调用 `mtrace()` 函数,启用内存泄漏检测功能。在程序结束时,调用 `muntrace()` 函数来关闭检测功能。通过这种方法,可以在程序运行时检查内存泄漏问题,并输出相应的检测结果。 3. 手动检查代码:手动检查代码是一种比较耗时、费力的方法,但是可以帮助程序员深入了解程序的内部实现。具体方法是,在程序中添加日志输出或者调试信息,记录每个内存分配和释放的位置、大小等信息,然后分析这些信息来确定是否存在内存泄漏问题。 需要注意的是,内存泄漏只是 Linux 内存管理中的一个问题,还有很多其他问题也需要注意,比如内存越界访问、使用未初始化的内存等问题。因此,在开发程序时,应该注意遵循良好的编程习惯,确保程序的健壮性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dokin丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值