我今天在服务器上遇到了一些问题,现在我已经把它归结为它无法摆脱遇到段错误的进程。
在进程出现seg-fault之后,进程会一直挂着,而不会被杀死。
应该导致错误Segmentation fault (core dumped)
。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *buf;
buf = malloc(1<<31);
fgets(buf, 1024, stdin);
printf("%s\n", buf);
return 1;
}
使用gcc segfault.c -o segfault && chmod +x segfault
编译和设置权限。
在有问题的服务器上运行此(并按下输入1次)会导致它挂起。我也在另一台具有相同内核版本(和大多数相同的软件包)的服务器上运行它,它得到了seg-fault然后退出。
以下是在两台服务器上运行strace ./segfault
后的最后几行。
服务器错误
"\n", 1024) = 1
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
# It hangs here....
工作服务器
"\n", 1024) = 1
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
root@server { ~ }# echo $?
139
当进程挂起时(在发生segfaulted之后),这就是它的外观。
无法
root@server { ~ }# ./segfault
^C^C^C
从ps aux进入
root 22944 0.0 0.0 69700 444 pts/18 S+ 15:39 0:00 ./segfault
cat / proc / 22944 / stack
[<ffffffff81223ca8>] do_coredump+0x978/0xb10
[<ffffffff810850c7>] get_signal_to_deliver+0x1c7/0x6d0
[<ffffffff81013407>] do_signal+0x57/0x6c0
[<ffffffff81013ad9>] do_notify_resume+0x69/0xb0
[<ffffffff8160bbfc>] retint_signal+0x48/0x8c
[<ffffffffffffffff>] 0xffffffffffffffff
另一个有趣的事情是我无法将strace
附加到挂起的段错误过程中。这样做实际上会让它被杀死。
root@server { ~ }# strace -p 1234
Process 1234 attached
+++ killed by SIGSEGV (core dumped) +++
ulimit -c 0
坐着,ulimit -c
,ulimit -H -c
和ulimit -S -c
都显示值0
3.10.0-229.14.1.el7.x86_64
Red Hat Enterprise Linux Server release 7.1 (Maipo)
服务器在其他所有方面都正常工作。
更新
关闭abrt(systemctl stop abrtd.service
)修复了在核心转储之后已经挂起的进程以及新进程核心转储的问题。再次启动abrt并没有带回问题。
更新2016-01-26 我们遇到的问题看起来很相似,但并不完全相同。用于测试的初始代码:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *buf;
buf = malloc(1<<31);
fgets(buf, 1024, stdin);
printf("%s\n", buf);
return 1;
}
挂了。 cat /proc/<pid>/maps
的输出是
00400000-00401000 r-xp 00000000 fd:00 13143328 /root/segfault
00600000-00601000 r--p 00000000 fd:00 13143328 /root/segfault
00601000-00602000 rw-p 00001000 fd:00 13143328 /root/segfault
7f6c08000000-7f6c08021000 rw-p 00000000 00:00 0
7f6c08021000-7f6c0c000000 ---p 00000000 00:00 0
7f6c0fd5b000-7f6c0ff11000 r-xp 00000000 fd:00 14284 /usr/lib64/libc-2.17.so
7f6c0ff11000-7f6c10111000 ---p 001b6000 fd:00 14284 /usr/lib64/libc-2.17.so
7f6c10111000-7f6c10115000 r--p 001b6000 fd:00 14284 /usr/lib64/libc-2.17.so
7f6c10115000-7f6c10117000 rw-p 001ba000 fd:00 14284 /usr/lib64/libc-2.17.so
7f6c10117000-7f6c1011c000 rw-p 00000000 00:00 0
7f6c1011c000-7f6c1013d000 r-xp 00000000 fd:00 14274 /usr/lib64/ld-2.17.so
7f6c10330000-7f6c10333000 rw-p 00000000 00:00 0
7f6c1033b000-7f6c1033d000 rw-p 00000000 00:00 0
7f6c1033d000-7f6c1033e000 r--p 00021000 fd:00 14274 /usr/lib64/ld-2.17.so
7f6c1033e000-7f6c1033f000 rw-p 00022000 fd:00 14274 /usr/lib64/ld-2.17.so
7f6c1033f000-7f6c10340000 rw-p 00000000 00:00 0
7ffc13b5b000-7ffc13b7c000 rw-p 00000000 00:00 0 [stack]
7ffc13bad000-7ffc13baf000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
但是,触发段错误的较小c代码(int main(void){*(volatile char*)0=0;}
)确实会导致段错误并且没有挂起...
答案 0 :(得分:2)
警告 - 此答案包含许多基于不完整信息的假设。希望它仍然有用!
当堆栈跟踪显示时,内核正忙于创建崩溃进程的核心转储。
但为什么这需要这么久?可能的解释是,您用于创建段错误的方法导致进程具有大量虚拟地址空间。
正如MM的评论所指出的,表达式1&lt;&lt; 31的结果未被C标准定义,因此很难说实际值传递给malloc,而是基于后续行为我假设它是一个很大的数字。
请注意,要使malloc成功,您没有必要在系统中实际拥有这么多内存 - 内核将扩展进程的虚拟大小,但实际的RAM只会在程序实际访问此RAM时分配。
我相信对malloc的调用成功,或者至少返回,因为你在按Enter键后声明它是段错误,所以在调用fgets之后。
在任何情况下,segfault都会引导内核执行核心转储。如果进程具有较大的虚拟大小,则可能需要很长时间,特别是如果内核决定转储所有页面,甚至是那些从未被进程触及的页面。我不确定它是否会这样做,但是如果它确实如此,并且如果系统中没有足够的RAM,则必须开始将页面进出内存以便将它们转储到核心转储中。这会产生很高的IO负载,这可能导致进程看起来没有响应(整体系统性能会降低)。
您可以通过查看abrtd转储目录(可能是/var/tmp/abrt
或检查/etc/abrt/abrt.conf
)来验证其中的一些内容,您可以在其中找到核心转储(或可能是部分核心转储)已创建。
如果您能够重现行为,那么您可以检查:
/proc/[pid]/maps
查看流程的地址空间地图,看看它是否真的很大sar
正在运行,那么即使在重新启动abrtd之前的一段时间内,您也可能会看到类似的信息。根据this bug report,无论ulimit设置如何,abrtd都会触发核心转储的收集。
有几种可能的解释。首先,它取决于系统中的可用RAM量。可能是大型进程的单个核心转储不会花费那么长时间,如果有足够的可用RAM并且系统没有被推送交换,则不会被视为挂起。
如果在您的初始实验中您在此状态下有多个进程,那么症状将比仅仅让单个进程出现行为异常的情况要糟糕得多。
另一种可能性是abrtd的配置已被更改但服务尚未重新加载,因此当您重新启动它时,它开始使用新配置,可能会改变它的行为。
yum更新也可能已更新为abrtd,但未重新启动它,因此当您重新启动它时,新版本正在运行。