/proc/sys/vm/max_map_count耗尽时,调用glibc 2.11.3 free()导致程序crash,问题追踪和解决
2017-12-15 14:45
639 查看
1. 问题
在客户环境下,当我们的程序消耗较大内存时,会偶尔crash。通过gdb分析,程序core dump在malloc/free函数内。
当把/proc/sys/vm/max_map_count 从65530增加到131070,程序再也不crash。
相关命令 echo 131073 > /proc/sys/vm/max_map_count
2. core dump 分析
Thread 1 (Thread 20074): #0 0x00007fbb77afd6d5 in raise () from/lib64/libc.so.6 #1 0x00007fbb77afecb1in abort () from /lib64/libc.so.6
#2 0x00007fbb77b43a0d in __malloc_assert () from /lib64/libc.so.6 #3 0x00007fbb787ea6a8 in
#3 不再给出,涉及保密
...
...
运行环境是SUSE 11 SP3, 其glibc 版本是2.11.3。
通过读glibc 2.11.3 代码,发现一个可疑点:
https://github.com/lzueclipse/learning/blob/master/c_cpp/glibc-2.11.3/malloc/malloc.c#L3552
当free()时,如果/proc/sys/vm/max_map_count 被耗尽,那么munmap系统调用会失败,就会导致assert,并core dump。
下面我们来试图证明这个分析是正确的。
3. 通过Google搜索找到一个类似的问题
Google搜索“munmap_chunk assert”,找到:https://sourceware.org/bugzilla/show_bug.cgi?id=13276
报的问题是:
When a process runs out of virtual mappings on Linux (more mmaps than /proc/sys/vm/max_map_count) then munmap can fail because it may need to split a mapping.
在这个bug下面,给出了一个重现这个bug的test case:
https://sourceware.org/bugzilla/attachment.cgi?id=5967
我在第4节中会利用这个测试用例重现bug。
针对这个bug的fix:
https://www.sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=6ef76f3b515c45a83f7831f806bf97a2af9ed008
- int ret __attribute__ ((unused)) = munmap((char *)block, total_size);
-
- /* munmap returns non-zero on failure */
- assert(ret == 0);
+ /* If munmap failed the process virtual memory address space is in a
+ bad shape. Just leave the block hanging around, the process will
+ terminate shortly anyway since not much can be done. */
+ munmap((char *)block, total_size);
}
可以看出,当munmap失败时,glibc不在assert,这个fix在glibc 2.15以及后续版本中。
4. 测试并重现bug
4.1 代码和测试环境
对https://sourceware.org/bugzilla/attachment.cgi?id=5967
进行了少量修改,以便我加入一些pmap调试信息,我的代码在:
https://github.com/lzueclipse/learning/blob/master/c_cpp/malloc_free_bug_2.11.3/mmap_malloc_test.c
测试将在SUSE 11 SP3(glibc 2.11.3)和SUSE 12(glibc 2.19)上进行,期望的行为是:
程序在SUSE 11 SP3 crash,但是在SUSE 12上运行正常。
4.2 在SUSE 11 SP3上的测试
1)ulimit -c unlimited不限制core file大小。
2) gcc -g mmap_malloc_test.c
编译,有符号。
3) ./a.out
cnt=65530, ps=4096
.........
19
.........
19
.........
19
.........
65531
a.out: malloc.c:3546: munmap_chunk: Assertion `ret == 0'failed.
Aborted (core dumped)
可以看出:
“/proc/sys/vm/max_map_count” 的值是65530,当超过它时(65531),crash在了free()。
pmap调试信息在:
https://github.com/lzueclipse/learning/blob/master/c_cpp/malloc_free_bug_2.11.3/pmap1.txt
https://github.com/lzueclipse/learning/blob/master/c_cpp/malloc_free_bug_2.11.3/pmap2.txt
https://github.com/lzueclipse/learning/blob/master/c_cpp/malloc_free_bug_2.11.3/pmap3.txt
https://raw.githubusercontent.com/lzueclipse/learning/master/c_cpp/malloc_free_bug_2.11.3/pmap4.txt
4) gdb -c ./core a.out
GNU gdb (GDB) SUSE (7.5.1-0.7.29)
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/robinwan/public_html/a.out...done.
[New LWP 9003]
Missing separate debuginfo for /lib64/libc.so.6
Try: zypper install -C
"debuginfo(build-id)=89f460a6502702332c336f3cd7f5568036483b98"
Missing separate debuginfo for /lib64/ld-linux-x86-64.so.2
Try: zypper install -C
"debuginfo(build-id)=38ab807fcca391af7d3ed7fcf585fbff2d54556a"
Core was generated by `./a.out'.
Program terminated with signal 6, Aborted.
#0 0x00007fd5f369db55 in raise () from /lib64/libc.so.6
(gdb) bt
#0 0x00007fd5f369db55 in raise () from /lib64/libc.so.6
#1 0x00007fd5f369f131 in abort () from /lib64/libc.so.6
#2 0x00007fd5f36e183d in __malloc_assert () from /lib64/libc.so.6
#3 0x000000000040097d in main () at mmap_malloc_test.c:42 《======42行,就是free().
4.3 在SUSE 12上的测试
./a.outcnt=65530, ps=4096
.........
19
.........
19
.........
19
.........
65531
.........
end!!!
“/proc/sys/vm/max_map_count” 的值是65530,当超过它时(65531),没有crash。
5.结论
一旦遇到类似问题,如果glibc版本是2.11.x ---2.14.x,只能增加“/proc/sys/vm/max_map_count”;从glibc 2.15.x,这个问题已经被修复。
相关文章推荐
- libxml2在iOS4上由于xmlFreeDoc导致程序Crash的解决方法
- 生产上数据库大量的latch free 导致的CPU资源耗尽的问题的解决
- 生产上数据库大量的latch free 导致的CPU资源耗尽的问题的解决
- wordcount程序出现map 100% reduce 0%问题的解决方法
- 使用coredata导致的版本更新后程序crash的问题
- windows命令行下随时随地调用csc编译器的方法(解决dos下编译.net程序找不到CSC的问题)
- 程序中和有js函数的网页交互,线程中调用 get_Script 就会错误的解决方法,由于COM的线程安全问题
- FTPClient调用retrieveFileStream导致线程挂起(防火墙问题);下载文件小于实际文件问题解决
- 在EXE程序中调用DLL文件后出现_CrtIsValidHeapPointer(pUserData)问题的解决方法
- glibc detected double free or corruption error问题解决
- 转载 Android解决通过Intent调用系统拍照程序,返回图片太小的问题
- 解决通过Intent调用系统拍照程序,返回图片太小的问题
- Websharp框架 在Win2003环境下 WinForm程序无法调用WebService问题解决
- VC程序调用MATCOM导致进程不自动结束的解决办法
- 解决通过Intent调用系统拍照程序,返回图片太小的问题
- 【转】 解决通过Intent调用系统拍照程序,返回图片太小的问题[android]
- 连接无法释放导致的程序问题解决
- java.lang.Process调用程序阻塞问题解决
- 使用coredata导致的版本更新后程序crash的问题
- 解决反复调用程序时变量的问题