如何从进程内部安全地转储Linux核心文件?

13

我们有一个使用C和C++编写的服务器,目前会捕获SEGV并将一些内部信息转储到文件中。我希望能够在捕获SEGV时生成核心文件并将其写入磁盘,这样我们的支持代表和客户就不必纠结于ulimit,也不用等待崩溃再次发生才能获取核心文件。过去我们曾经使用abort函数,但它受制于ulimit规则,没有什么帮助。

我们有一些遗留代码读取/proc/pid/map并手动生成核心文件,但它已经过时,并且似乎不太可移植(例如,我猜测它在64位版本中可能无法工作)。在Linux进程中生成并转储核心文件的最佳方法是什么?


我怀疑这是不可能的,因为ulimit规则禁止创建核心转储。 - Johannes Schaub - litb
2
@JohannesSchaub-litb 其实很有可能。您只需使用 *setrlimit(2)*,将第一个参数(“资源”)传递为 RLIMIT_CORE 即可。该函数的反函数是 *getrlimit(2)*,它们都非常有用。 - Pryftan
8个回答

9
谷歌有一个库,可以在运行中的进程内生成核心转储,称为google-coredumper。这应该会忽略ulimit和其他机制。
生成核心文件的调用文档在这里。根据文档,似乎可以在信号处理程序中生成核心文件,尽管不能保证始终有效。

这看起来非常有前途!我明天会试一试。 - Jeremy Mullin
+1,看起来非常方便,非常容易使用,并且有一个非常不受限制的许可证 :) - Tim Post

5
我看到pmbrett的帖子,想:“嘿,那很酷”,但在我的系统(Gentoo)上找不到那个实用程序。
因此,我进行了一些探索,并发现GDB中有这个选项。
gdb --pid=4049 --batch -ex gcore

对我来说似乎很好用。

但是它并不是非常有用,因为它只能捕获在此时使用的最低函数,但在此之外仍然可以很好地工作(没有内存限制,可以使用它来转储火狐进程的 350M 快照)。


2
FYI,“linux”命令“gcore”实际上是RedHat Linux命令。这是一个Bourne shell脚本,调用gdb并使用gdb的gcore命令生成核心文件。 - Michael Snyder

4

尝试使用Linux命令gcore

用法: gcore [-o 文件名] pid

您需要使用system(或exec)和getpid()来构建正确的命令行,以便从进程内部调用它。


3

以下是解决此问题的几种可能方法:

  1. 修复ulimit!!!
  2. 接受您无法获得核心文件并在gdb内运行的事实,脚本化执行“thread all apply bt”以处理SIGSEGV
  3. 接受您无法获得核心文件并从应用程序内获取堆栈跟踪。《Linux Journal》上的Stack Backtracing Inside Your Program文章已经过时,但现在也应该可以使用。

感谢提供backtrace的链接。在帮助客户时,我们目前已经在进行1和2的操作,但我正在寻找一种更自动化的方式来获取核心文件。在Windows中,我们调用一个API来生成转储文件并自动检索它。我希望在我们的Linux版本中有类似的功能。 - Jeremy Mullin

2

您还可以使用setrlimit(2)在程序中更改ulimit()。与ulimit shell命令一样,这可以将限制降低或将其提高到硬限制允许的程度。在启动时设置setrlimit()以允许核心转储,就可以了。


请务必检查错误。如果您想验证它(而无需运行其他代码并检查错误等),则可以使用*getrlimit(2)*进行反向验证。 - Pryftan

2

我假设你有一个信号处理程序来捕获SEGV信号,例如打印消息并调用_exit()函数。否则,你一开始就会有一个核心文件!你可以像下面这样做。

void my_handler(int sig)
{
   ...
   if (wantCore_ && !fork()) {
      setrlimit(...);  // ulimit -Sc unlimited
      sigset(sig, SIG_DFL);  // reset default handler
      raise(sig);  // doesn't return, generates a core file
   }
   _exit(1);
}

更好的做法是在设置信号处理程序时更改限制,同时应该检查*setrlimit(2)的错误。如果是我记得的同一个信号,它将再次被捕获(我不确定,但我相信是这样)。但最终,setrlimit(2)*确实是正确的方法。 - Pryftan

1
使用backtrace和backtrace_symbols glibc调用来获取跟踪信息,但请记住backtrace_symbols在内部使用malloc,在堆破坏的情况下可能会失败。

这可能是一个不太为人知的功能,但问题在于它不会生成核心文件,也不是被要求的内容。例如,如果您想打印回溯信息,那么这很棒,但这也不是同一件事情。然而,如果您不想编写自己的堆栈日志系统,并且还想在运行时获取信息,那么这肯定是一个有用的功能。无论如何,在将近9年后,我给它点个赞。 - Pryftan

0

系统("kill -6 ")

如果你还在寻找什么东西,我可以试一试。


这种方法的问题在于它不会像崩溃时那样包含堆栈等信息。进程死亡的唯一原因是直接发送了 SIGABRT 信号(这是更好的方式,即 *kill -SIGABRT*)。而 SIGSEGV 不同之处在于访问了地址空间外的内存。因此,尽管它会生成一个核心转储文件(至少如果 ulimit 允许的话),但这并不是同一件事情。 - Pryftan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接