草庐IT

c++ - 分段故障本身挂起

coder 2023-06-02 原文

我今天遇到了一些服务器问题,现在我将其归结为它无法摆脱出现段错误的进程。

在进程出现 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

当进程挂起时(在它发生段错误之后),这就是它的样子。

无法 ^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坐下,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)
  • 在 vmware 中运行

服务器在其他所有方面都在正常工作。

更新 关闭 abrt (systemctl stop abrtd.service) 解决了核心转储后进程已挂起的问题,以及新进程核心转储。再次启动 abrt 并没有恢复问题。

2016 年 1 月 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;} ) 确实导致了段错误并且没有挂起...

最佳答案

警告 - 此答案包含许多基于手头不完整信息的假设。希望它仍然有用!

为什么段错误似乎挂起?

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

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

正如 M.M. 的评论中所指出的,表达式 1<31 的结果在="" c="" 标准中是未定义的,因此很难说传递给="" malloc="" 的实际值是什么,但基于后续行为我我假设这是一个很大的数字。="">

请注意,为了使 malloc 成功,您实际上不需要在系统中拥有这么多 RAM - 内核将扩展您进程的虚拟大小,但只有在您的程序实际访问该 RAM 时才会分配实际 RAM。

我相信对 malloc 的调用会成功,或者至少会返回,因为您在按 Enter 后声明它会出现段错误,因此在调用 fgets 之后也是如此。

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

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

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

  • /proc/[pid]/maps查看进程的地址空间映射,看看是否真的很大
  • 使用 vmstat 之类的工具查看系统是否正在交换、正在进行的 I/O 量以及经历了多少 IO 等待状态
  • 如果您运行了 sar,那么即使在重新启动 abrtd 之前的时间段内,您也可能会看到类似的信息。

为什么会创建核心转储,即使 ulimit -c 为 0?

根据this bug report ,无论 ulimit 设置如何,abrtd 都会触发核心转储的收集。

为什么再次启动 arbtd 后这种情况没有再次发生?

对此有几种可能的解释。一方面,它取决于系统中可用 RAM 的数量。如果有足够的空闲 RAM 并且系统没有被推送到交换,那么大型进程的单个核心转储可能不会花费那么长时间,并且不会被视为挂起。

如果在您最初的实验中,您有多个处于这种状态的进程,那么症状会比仅让一个进程出现异常时的情况严重得多。

另一种可能性是 abrtd 的配置已更改但服务尚未重新加载,因此当您重新启动它时,它开始使用新配置,可能会改变它的行为。

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

关于c++ - 分段故障本身挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33673592/

有关c++ - 分段故障本身挂起的更多相关文章

  1. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  6. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

  7. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

    出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

  8. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

    我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

  9. ruby - rails 3.2.2(或 3.2.1)+ Postgresql 9.1.3 + Ubuntu 11.10 连接错误 - 2

    我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat

  10. ruby - 在 Ruby + Chef 中检查现有目录失败 - 2

    这是我在ChefRecipe中的一blockRuby:#ifdatadirdoesn'texist,moveoverthedefaultoneif!File.exist?("/vol/postgres/data")execute"mv/var/lib/postgresql/9.1/main/vol/postgres/data"end结果是:Executingmv/var/lib/postgresql/9.1/main/vol/postgres/datamv:inter-devicemovefailed:`/var/lib/postgresql/9.1/main'to`/vol/post

随机推荐