遇到了嵌入式linux下面系统级内存泄露问题。跟踪问题日志。
平台:ARM9
内核:2.6.22
问题:execl造成系统内存减少,通过cat /proc/memoinfo 发现少的内存并且都移往slab中去了
测试代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int i = 0;
int pid = 0;
for (i=0; i<1000; i++)
{
pid = fork();
if (pid < 0)
{
printf("fork() fail!
");
continue;
}
else if (pid == 0) //child
{
//system("ls");
execl("/bin/ls", "ls", "", NULL);
printf("execl() here!
");
exit(0);
}
else
{
waitpid(pid, 0, 0);
printf("[%d] exited!", pid);
}
printf("%d
", i);
}
return 0;
}
20091010:在PC上ubuntu 9.04上测试这个程序发现没有内存减少的现象。在ARM9上发现,每次执行1000次左右,导致有300k左右的内存减少。可能与内核版本(ubuntu 9.04是2.6.30的kernel版本)有关系,通过google或百度并没有发现有这样的问题。估计是配置内核导致的问题。下一步将通过cat /proc/slabinfo 来研究内存多出来的部分的来源。
20091011:通过1000此调用之后,通过cat /proc/slabinfo对比发现,变化最大的是
name : tunables : slabdata
size-256 120 120 256 15 1 : tunables 120 60 0 : slabdata 8 8 0
size-256 1120 1125 256 15 1 : tunables 120 60 0 : slabdata 75 75 0
计算,pagesperslab为1,即一个page中有一个slab,(75-8)×4K = 268k左右。为了排除其他部分造成的内存泄露。做了一个测试,频繁的进行1000次malloc和free,发现size-256项也无特别变化。可以初步判断,应该是size-256块slab中出现了内存没有释放的行为。下一步动作,研究源代码do_execve部分来看分配192~256k的内存的嫌疑函数以及是否在退出后被释放。
20091012:为了排除slab本身有bug的情况,将内核分配机制改为slub,结果,依然在执行1000次之后少出300k的内存空间。在slub.c中,static struct kmem_cache *get_slab(size_t size, gfp_t flags)函数中插入log,这里是分配的必经之路。当分配的大小在128~256之间时,log出分配的size大小。执行测试程序,发现
_DEBUG: get slab size = 192
_DEBUG: get slab size = 208
就是说有分配192和208的需求在。
而简单的ls,或pwd都会打印这个出来,本人据此推断,分析进行shell命令也会有内存泄露问题。手动ls100次,果不其然,大概有52k的内存泄露,平均起来就是2个256的空间没有收回。
对log的出处一直跟踪(callstack),发现最终锁定在load_elf_binary这个函数中,仔细看了code,发现log不是很规范,与同事交流了,原来被touch过。立即找到kmalloc和kfree的地方,看哪个kfree有没有漏掉,果然发现漏洞。去http://lxr.linux.no对比2.6.22历史版本,发现同事的修改有个地方忘记free了,兴奋之余,赶紧改过来,测试5000次。结果正常。原来这个添加的code是同事用来调试用的,忘记砍掉了。我说呢,内核应该没有这样明显的错误的,google也无任何关于此的问题。经过此次跟踪后,对内核内存分配更加理解。