vmalloc kmalloc slab native和kernel 内存泄漏

从前面的介绍已经看出,这两个函数所分配的内存都处于内核空间,即从3GB~4GB;但位置不同,kmalloc()分配的内存处于3GB~high_memory之间,而vmalloc()分配的内存在VMALLOC_START~4GB之间,也就是非连续内存区。一般情况下在驱动程序中都是调用kmalloc()来给数据结构分配内存,而vmalloc()用在为活动的交换区分配数据结构,为某些I/O驱动程序分配缓冲区,或为模块分配空间,例如在include/asm-i386/module.h中定义了如下语句: #define module_map(x) vmalloc(x) 其含义就是把模块映射到非连续的内存区

kernel space 使用的memory 通常包括 kernel stack, slub, page table, vmalloc, shmem 等

内核栈大小80M,每个进程占用8K的内核栈,占用8M的用户栈。

从进程的角度来讲, 通常情况下进程所使用的memory, 都会通过mmap 映射到进程空间后访问使用

native /proc/pid/maps memory maps

1. malloc

/proc/pid/maps看到多出很多[anon:libc_malloc] 打开android malloc debug机制复现问题

2.mmap

coredump adb shell procrank -u > procrank.txt

3.ashmem

4.thread泄漏

活线程泄漏:大量创建线程,而且这些线程都是活着的。这种情况要避免出现,需要优化。
僵尸线程泄漏:线程创建时没设定deteched状态,完成任务退出后没有其他线程回收(调用pthread_jion函数)

kernel

1.触发OOM会看到是kernel stack,slab泄漏

2.kmemleak

创建一个进程每10分钟(默认)扫描系统内存然后打印泄漏的内存的信息。

cat sys/kernel/debug/kmemleak

3.buddy内存泄漏

page owner会记录每个物理页的分配调用栈等信息。通过这些信息,可以排查buddy内存泄漏或碎片化问题。通过对调用栈进行合并排序,可以列出分配总量大的调用栈,然后剔除不是泄漏的信息,基本能看出泄漏点。

buddy system是kernel初始化完成后,最基础的分配器,如果出现leak,可以打开page owner跟踪每个物理页的使用情况。

4.slab 用途:inode_cache、dentry

cat /proc/slabinfo 查看所有kmem_cache的信息,

slab_unreclaimable:717776kB kernel_stack:1160080kB
slub占用了717M,超出正常水平,kernel_stack / 16KB = 72505个线程,也远远超出正常水平。
刚好这题有mrdump,查看nr_threads,值为1000,和上面算出来的值不一致。那么必然存在内存泄漏。
问题性质变成:内存泄漏,而且是kernel stack和slub内存泄漏。
检查下kernel stack内存哪里来的,是直接从buddy system要的。而kernel stack只有在进程退出才会释放。是否存在进程没有退出?不可能,因为nr_threads正常。
3. 猜测
经过代码分析,怀疑task_struct里的usage计数在进程退出后不为0导致泄漏。而task_struct内存来自slub,这就解释了为何slub也存在内存泄漏了。
如何排查task_struct泄漏呢?task_struct里有usage引用计数。通过get_task_struct/put_task_struct来控制。那么这题一定是有人get_task_struct后没有put_task_struct了。
4. 如何验证这个猜测?
因为这题有mrdump,如果有task_struct泄漏,那么一定有很多task_struct在里面,并且状态是僵尸。
task_struct都是通过task_struct_cachep来申请的,只要遍历所有的属于task_struct_cachep的内存,观察里面的内容,就能知道是否存在僵尸,有多少个僵尸,是否和上面推导的内存泄漏吻合。
总共有70000多个线程处于TASK_DEAD状态,基本上和上面的推导是吻合的。接下来要审核代码,查看哪里get_task_struct却没有put_task_struct
6. 审核代码
仔细审查最近提交的代码,终于找出有一处添加的代码去get_pid_task,这个函数会去get_task_struct,代码提交者没有注意用完后需要put_task_struct,从而导致了内存泄漏。
并且这处代码会被频繁执行到。
7. 修复代码验证
临时先回退代码,然后再次复现抓取mrdump,用上面的cmm脚本复验,发现已没有TASK_DEAD线程了,表示问题就是这一处引起。
根本原因
添加的新功能代码有问题,调用了get_pid_task,这个函数会去get_task_struct,但用完后没有put_task_struct。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值