分割故障本身就是悬而未决的

我今天在服务器上遇到了一些问题,现在我已经把它解决了,因为它无法摆脱出现段错误的进程。

在这个过程得到一个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编译和设置权限gcc segfault.c -o segfault && chmod +x segfault

运行这个(并按下input1次),在有问题的服务器上导致它挂起。 我也使用相同的内核版本(和大部分相同的软件包)在另一台服务器上运行这个服务,并且得到了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 

当进程挂起(在它已经分离之后)时,这就是它的样子。

不能够^ c它

 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是sat和ulimit -culimit -H -culimit -S -c都显示值0

  • 内核版本: 3.10.0-229.14.1.el7.x86_64
  • Distro版本: Red Hat Enterprise Linux Server release 7.1 (Maipo)
  • 在vmware中运行

服务器正如其他所有应用程序一样工作。

更新closuresabrt( systemctl stop abrtd.service )解决了在core-dump之后已挂起的进程以及新进程core-dumping的问题。 再次启动abrt没有带回问题。

更新2016-01-26我们得到了一个类似的问题,但不完全相同。 用于testing的初始代码:

 #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;} )触发段错误确实导致段错误,并没有挂起…

警告 – 这个答案包含了许多基于不完整信息的假设。 但希望它仍然有用!

为什么段错误似乎挂起?

如堆栈跟踪所示,内核正忙于创建崩溃进程的核心转储。

但为什么这么久呢? 一个可能的解释是,用于创建段错误的方法导致进程拥有大量的虚拟地址空间。

正如在MM的评论中指出的那样,表达式1 << 31的结果在C标准中是未定义的,所以很难说实际的价值被传递给malloc,但是基于后来的行为,我假设它是一个很大的数字。

请注意,要使malloc成功,您不必在系统中实际使用这么多RAM – 内核将扩展进程的虚拟大小,但实际RAM只会在程序实际访问此RAM时分配。

我相信对malloc的调用会成功,或者至少会返回,因为你在按下回车之后声明它是段错误的,所以在调用fgets之后。

在任何情况下,segfault都会引导内核执行核心转储。 如果进程的虚拟大小很大,那么可能需要很长时间,特别是如果内核决定转储所有页面,甚至那些从未被进程触及的页面。 我不确定是否会这样做,但是如果这样做了,并且系统中没有足够的RAM,就必须开始交换页面内存和内存页面,以便将它们转储到核心转储。 这会产生很高的IO负载,这可能导致进程看起来没有响应(并且整个系统性能会降低)。

您可以通过查看abrtd转储目录(可能是/var/tmp/abrt ,或者检查/etc/abrt/abrt.conf ),在那里找到核心转储(或者可能是部分核心转储)已经创建。

如果你能够重现行为,那么你可以检查:

  • /proc/[pid]/maps来查看进程的地址空间映射,看它是否真的很大
  • 使用像vmstat这样的工具来查看系统是否正在交换,正在进行的I / O的数量以及正在经历多少IO等待状态
  • 如果你已经开始运行,那么即使在重启abrtd之前,你也可能看到类似的信息。

为什么创建了一个核心转储,即使ulimit -c是0?

根据这个错误报告 ,无论ulimit设置如何,abrtd都会触发收集一个核心转储。

为什么当arbtd再次启动时,这不会再次发生?

对此有几个可能的解释。 首先,这取决于系统中可用RAM的数量。 如果有足够的可用RAM并且系统不被推送交换,那么大型进程的单个核心转储可能不会花费那么长时间,也不会被认为是挂起的。

如果在最初的实验中,你在这个状态下有几个进程,那么症状就会比仅仅是一个进程失败的情况更糟。

另一种可能是,abrtd的配置已经被修改,但服务还没有被重新加载,所以当你重新启动它时,它开始使用新的配置,可能会改变它的行为。

yum更新也可能更新了abrtd,但没有重新启动它,所以当你重新启动时,新版本正在运行。