着急不想看分析过程的,可以直接跳到 0x05总结 看解决办法
0x00 发生了什么
最近 杨海象 负责开发的某平台上线,效果不错,但是在使用期间出现了核心转储,占用了大量磁盘空间,如下图,可见,近1T。
平台上线当天杨海象和老王尝试关闭核心转储,执行了命令
ulimit -c 0
核心仍然继续生成。虽然磁盘空间够用,且平台运行正常,但是这个问题找不到原因,且无法解决核心一直产生的问题。头疼,终于,他来了,他带着一杯7分甜的奶茶来找我了。
0x01 什么是核心转储
百度百科解释的还不错,我稍微修改一点:
在UNIX系统中,常将“主内存”(main memory) 称为核心(core),因为在上世纪,计算机还没使用半导体作为内存材料之前,内存的实现便是使用的核心(core)。而核心映像(core image) 就是 内存中的“进程”(process)当前正在执行的内容。当进程发生错误或意外收到指定“信号”(signal) 而终止执行时,系统会将核心映像写入一个文件,以供开发人员跟踪调试错误之用。这就是所谓的核心转储(core dump)。
核心转储简单解释就是当一个程序崩溃时,在该进程当前工作目录的core文件中复制了该进程的存储图像。core文件仅仅是一个内存映象(同时加上调试信息),主要是用来调试的。
0x02 核心转储产生的原因及相关知识
1.进程收到特定信号指令而终止1,便会核心转储。
部分信号(核心转储)说明:
符号常量 | 说明 | 缺省动作 |
---|---|---|
SIGABRT | 非正常终止信号 | 终止w/core |
SIGBUS | 获取内存中不确定的部分 | 终止w/core |
SIGEMT | 硬件故障 | 终止w/core |
SIGFPE | 算术异常 | 终止w/core |
SIGILL | 无效硬件信号的检测 | 终止w/core |
SIGIOT | 硬件故障 | 终止w/core |
SIGQUIT | 交互式终止信号 | 终止w/core |
SIGSEGV | 无效内存引用检测信号 | 终止w/core |
SIGSYS | 无效系统调用 | 终止w/core |
SIGTRAP | 软中断 | 终止w/core |
SIGXCPU | 超过CPU限制(setrlimit) | 终止w/core |
SIGXFSZ | 超过文件长度限制(setrlimit) | 终止w/core |
2.POSIX标准
可移植操作系统界面(Portable Operating System Interface,POSIX),是IEEE为运行在UNIX系统上的程序而定义的一系列互相关联的API标准的总称。POSIX这个名称基本上是Portable Operating System Interface(可移植操作系统接口)的缩写,而X则表明其对Unix API的传承。
由于以前各应用开发厂商自己玩自己的C语言,互相之间不兼容,于是出现了该标准,统一了C语言语法等,同时规定了所有信号常量及对应值。
(根据记忆写的,如果说错了的话就评论告诉我吧,我把这段删了)
3.信号
懒得写了,可以阅读<signal.h>头文件,它声明了sigset_t类型和sigaction结构。定义了三类信号:
(1)必需的信号;
(2)任务控制信号;
(3)内存保护信号。
略了。。。
0x03 核心转储复现
1.编写demo程序制造核心转储
2.查看当前ulimit的设置,并开启核心转储
执行命令
ulimit -a
查看配置情况,根据输出信息对ulimit进行设置操作:
ulimit -c unlimited
将核心转储文件的大小设置为无限大,即开启核心转储
3.编译并执行demo,复现核心转储
执行demo,核心文件自动转储到当前工作目录
4.调试core文件,追踪调用栈,分析错误点及错误原因
可以看到错误发生在null_ptr()函数中,在文件第14行,错误原因是信号SIGSEGV。产生信号SIGSEGV的可能原因是数组越界,空指针解引用等。
0x04 修改ulimit配置不生效问题分析
1.情景分析
平台上线,使用时,生成了大量内核转储文件,但平台运行正常,老王和杨海象担心磁盘被占满,所以决定先关闭内核转储。
执行
ulimit -c 0
再查看
ulimit -a
输出如下
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 63157
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 65535
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 63157
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
可是发现仍有核心文件在转储。持续在生成。直到活动结束,平台未出现崩溃,因此该问题未当天处理完。
2.问题分析
由于核心转储文件生成的时候是用的pid做后缀,不知道崩溃的是哪个进程/线程。但是根据file的提示信息知道具体是哪个进程/线程崩溃:
这里给的图是测试demo,分析了生产环境产生的核心转储,知道了是哪个进程,老王告诉我这个进程是由守护进程拉起的,也存在主动kill的情况。ok,大胆猜测一下核心转储产生的原因可能就是主动kill,一会儿去gdb看看,先分析下为什么设置了
ulimit -c 0
却仍核心转储。
是否是由于崩溃进程不是终端shell拉起(进程),而是作为一个线程执行的。
3.问题复现
编写python起线程验证上述猜想:
demo编写完成后,先确认环境:
(1)关闭核心转储;
(2)验证是否关闭。
环境确认无误后,运行demo验证猜想
转储文件未生成,猜想错误。继续分析和猜想。
首先分析ulimit命令,这个命令事shell内置的命令,所以是否和shell有关?那开始验证:
新开一个shell,cd到同目录,查看ulimit属性,两个终端的ulimit属性配置不同(file size),如下图:
则先在file size = 0的终端(右)上先运行python demo,发现未生成核心转储;同时在终端(右)demo结束前,让file size = unlimited的终端(左)运行demo,发现核心转储了,说明核心转储的配置属性与当前终端相关。
0x05 总结
1.问题原因
阿西吧,终于搞清楚了。ulimit命令设置的是当前终端的属性,要想全局修改,则需要修改以下两个文件中的一个2
/proc/sys/kernel/core_pattern
/proc/sys/kernel/core_uses_pid
有个小坑,就是这俩文件需要root权限修改,且vi不能修改该文件,可以使用nano或者echo重定向。
2.解决办法
修改ulimit配置,使所有核心转储保存到 /dev/null
echo "/dev/null/core.%p" > /proc/sys/kernel/core_pattern
下班,回家,喂狗,睡觉。