开启辅助访问 切换到窄版

打印 上一主题 下一主题

智汇华云|内核调试分享:Linux内核内存泄漏了

[复制链接]
作者:明天∑再来 
版块:
嵌入式操作系统 linux 发布时间:2020-3-23 16:10:50
8810
楼主
跳转到指定楼层
| 只看该作者 回帖奖励 |倒序浏览 |阅读模式


近期华云数据公有云上线了安卓云,客户测试反馈经常内存跑满了,导致安卓程序被杀死。
但是我们内部运行几周并没有出现问题,询问客户得知是打开某音APP播放视频,什么也不用操作,逐渐内存就被耗光了。
基于此,本期“智汇华云”邀请到华云数据计算网络部总监仇大玉为大家带来内核调试分享,带大家一同解密Linux内核内存泄漏。
本期嘉宾
华云数据计算网络部总监 仇大玉

让我们先来查看free:



6G的内存(实际5.8G),free只有210M了,通过统计SLAB的内存量:echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB
一查slabinfo发现slab就占据了1个多G,在部分3G的手机中,slab能高达1.9G。
太恐怖,再看/proc/meminfo里面确实slab占用很多(一下是在另外一台云手机中操作)。


尝试echo 3 > /proc/sys/vm/drop_caches 释放cache,发现基本没有用,一查在看到基本都是SUnreclaim,根本不能被释放,很显然出现了内核内存泄漏。
同时查看slabinfo: cat /proc/slabinfo


kmalloc-256占用1.2G左右,128也占用了600M左右。
尝试打开slab trace: echo 1 > /sys/kernel/slab/kmalloc-256/trace,竟没有成功, cat /sys/kernel/slab/kmalloc-256/trace 依然是0.
没办法,只能重新编译内核,打开SLAB的debug以及内核做内存泄漏检测的开关KMEMLEAK:
+CONFIG_SLUB_DEBUG=y
+CONFIG_SLUB_DEBUG_ON=y
+DEBUG_KMEMLEAK=y
重新编译安卓内核(我们安卓内核是基于upstream内核自己定制的,不是原生安卓的内核)
重新打开slab trace: echo 1 > /sys/kernel/slab/kmalloc-256/trace
终于可以打开了,然后打开某音app开始播放视频。大概一分钟后,安卓里面 dmesg得到很多debug的Trace,命名为dmesg_218,
然后便于寻找:
grep "TRACE kmalloc-256 alloc" dmesg_218 | awk '{print $6}' | sort > alloc.txt
grep "TRACE kmalloc-256 free" dmesg_218 | awk '{print $6}' | sort > free.txt
再打开这两个文件对比:vim alloc.txt free.txt -O2


发现alloc要明显多于free,有些例如黄色显示部分alloc 4次,只free了3次。很明显是有内存没有被释放,在dmesg里找到这个地址,查看是哪里alloc的没有被释放:


追踪到最后发现是这边,只有alloc没有对应的free。
暂时找到问题了,通过刚才打开的kmemleak:cat /sys/kernel/debug/kmemleak确认一下:


追踪到大量的都是sw_sync_ioctl这个函数中申请的都没有被释放。至此形式明朗了许多。
找到内核中这个函数:vim ./drivers/dma-buf/sw_sync.c
调用链是sw_sync_ioctl-->sw_sync_ioctl_create_fence-->sync_pt_create。
在sync_pt_create函数中调用了pt = kzalloc(sizeof(*pt), GFP_KERNEL);而kzalloc会调用slab分配器分配内存。然后查找这个pt在哪释放了。通篇源码中没有找到,但是找到了:
timeline_fence_release中调用了fence_free(fence),而fence正式pt里面的对像,怎么回事?没有释放pt,但是释放了fence?
看下pt结构体:
/**
* struct sync_pt - sync_pt object
* @base: base fence object
* @link: link on the sync timeline's list
* @node: node in the sync timeline's tree
*/
struct sync_pt {
struct fence base;
struct list_head link;
struct rb_node node;
};
发现没有,base地址就是pt的地址,但是承载的对象不一样。继续追踪,发现timeline_fence_release是static const struct fence_ops timeline_fence_ops的release函数实现,也就是fence->ops中的release,查遍源码:


而fence_relase只有fence_put调用了:


查到此,发现fence_put会在fence的refcount减一后如果为0调用释放内存。
回到sw_sync这个驱动,竟然没有地方调用fence_put(其实是有一个地方的就是在创建pt后创建sync_file如果创建失败会去调用fence_put),这也就奇怪为何内存泄漏了。
改动方案(fence主要用户是安卓的图形相关HWCompose,不再赘述),这边在内核端每个fence被signal后显示调用fence_put,验证后没有问题,没有内存泄漏了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表