如何在Linux发生分段故障时生成核心转储?


Answers:


249

这取决于您使用的外壳。如果使用的是bash,则ulimit命令控制与程序执行有关的一些设置,例如是否应该转储core。如果您输入

ulimit -c unlimited

那么这将告诉bash它的程序可以转储任何大小的内核。您可以根据需要指定诸如52M的大小,而不是无限制的大小,但是实际上,这不是必需的,因为核心文件的大小可能永远不会成为您的问题。

在tcsh中,您可以输入

limit coredumpsize unlimited

21
@lzprgmr:要澄清:默认情况下不生成核心转储的原因是未设置限制和/或将限制设置为0,这会阻止转储核心。通过设置无限制限制,我们保证始终可以生成核心转储。
伊莱·考特赖特

6
链接更深入,并提供了更多选项来启用Linux中的核心转储。唯一的缺点是某些命令/设置无法解释。
莎莎,

6
在bash 4.1.2(1)上,无法指定发行限制(例如52M),从而导致数字错误消息无效。手册页指出“值以1024字节为增量”。
2012年

4
好吧,我有一个“小” OpenGL项目,该项目曾经做过一些奇怪的事情,并导致X服务器崩溃。当我重新登录时,我看到了一个可爱的17 GB核心小文件(在25 GB分区上)。限制核心文件的大小绝对是个好主意:)
IceCool 2013年

1
@PolarisUser:如果要确保您的分区不会被吃掉,我建议将限制设置为1个演出。那应该足够大以处理任何合理的核心转储,同时又不威胁要用完所有剩余的硬盘空间。
Eli Courtwright 2014年

60

如上所述,这里要问的真正问题是如何在未启用核心转储的系统上启用核心转储。在这里回答了这个问题。

如果您来这里是为了学习如何为挂起的进程生成核心转储,答案是

gcore <pid>

如果gcore在您的系统上不可用,则

kill -ABRT <pid>

不要使用kill -SEGV,因为这通常会调用信号处理程序,这使得诊断阻塞进程更加困难


我认为-ABRT与相比-SEGV,调用信号处理程序的可能性更大,因为中止比段故障更有可能恢复。(如果您处理段错误,通常它会在处理程序退出后立即再次触发。)生成核心转储的更好信号选择是-QUIT
celticminstrel

32

要检查在哪里生成核心转储,请运行:

sysctl kernel.core_pattern

要么:

cat /proc/sys/kernel/core_pattern

%e进程名称和%t系统时间在哪里。您可以更改它/etc/sysctl.conf并通过重新加载sysctl -p

如果未生成核心文件(通过sleep 10 &和进行测试killall -SIGSEGV sleep),则通过以下方式检查限制ulimit -a

如果您的核心文件大小有限,请运行:

ulimit -c unlimited

使它不受限制。

然后再次测试,如果核心转储成功,则在分段错误指示之后您将看到“(核心转储)”,如下所示:

分段错误:11(核心已转储)

另请参阅:核心转储-但核心文件不在当前目录中?


的Ubuntu

在Ubuntu中,核心转储由Apport处理,可以位于中/var/crash/。但是,默认情况下在稳定版本中禁用该功能。

有关更多详细信息,请检查:在Ubuntu哪里可以找到核心转储?

苹果系统

对于macOS,请参阅:如何在Mac OS X中生成核心转储?


3
对于Ubuntu,要快速恢复正常行为(在当前目录中转储核心文件),只需使用“ sudo service apport stop”停止apport服务即可。还要注意,如果您在docker中运行,则该设置是在主机系统上而不是在容器内控制的。
Digicrat

26

最后,我所做的就是在崩溃之前将gdb附加到该进程,然后在出现段错误时执行了generate-core-file命令。强制生成核心转储。


您如何将gdb附加到进程中?
加尼

6
要回答Ritwik G,要将进程附加到gdb,只需启动gdb并输入'attach <pid>',其中<pid>是要附加的进程的pid。
Jean-Dominique Frattini 2013年

(缩写为ge
user202729

如果他们有一个新问题,则应提出一个新问题,而不要发表评论。
user202729

奇怪的是我已经设置ulimit -cunlimited,但是仍然没有创建generate-core-file核心文件,gdb会话中的文件确实创建了核心文件,谢谢。
CodyChan

19

也许您可以用这种方式做到这一点,该程序演示了如何捕获分段错误并炮轰到调试器(这是底下使用的原始代码AIX),并打印堆栈跟踪直至分段错误。您将需要更改sprintf用于gdbLinux 的变量。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>

static void signal_handler(int);
static void dumpstack(void);
static void cleanup(void);
void init_signals(void);
void panic(const char *, ...);

struct sigaction sigact;
char *progname;

int main(int argc, char **argv) {
    char *s;
    progname = *(argv);
    atexit(cleanup);
    init_signals();
    printf("About to seg fault by assigning zero to *s\n");
    *s = 0;
    sigemptyset(&sigact.sa_mask);
    return 0;
}

void init_signals(void) {
    sigact.sa_handler = signal_handler;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction(SIGINT, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGSEGV);
    sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGBUS);
    sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGQUIT);
    sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGHUP);
    sigaction(SIGHUP, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGKILL);
    sigaction(SIGKILL, &sigact, (struct sigaction *)NULL);
}

static void signal_handler(int sig) {
    if (sig == SIGHUP) panic("FATAL: Program hanged up\n");
    if (sig == SIGSEGV || sig == SIGBUS){
        dumpstack();
        panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown"));
    }
    if (sig == SIGQUIT) panic("QUIT signal ended program\n");
    if (sig == SIGKILL) panic("KILL signal ended program\n");
    if (sig == SIGINT) ;
}

void panic(const char *fmt, ...) {
    char buf[50];
    va_list argptr;
    va_start(argptr, fmt);
    vsprintf(buf, fmt, argptr);
    va_end(argptr);
    fprintf(stderr, buf);
    exit(-1);
}

static void dumpstack(void) {
    /* Got this routine from http://www.whitefang.com/unix/faq_toc.html
    ** Section 6.5. Modified to redirect to file to prevent clutter
    */
    /* This needs to be changed... */
    char dbx[160];

    sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname);
    /* Change the dbx to gdb */

    system(dbx);
    return;
}

void cleanup(void) {
    sigemptyset(&sigact.sa_mask);
    /* Do any cleaning up chores here */
}

您可能还需要添加一个参数来获取gdb来转储核心,如本博客此处所示


16

还有更多因素可能会影响核心转储的生成。我遇到了这些:

  • 转储的目录必须可写。默认情况下,这是进程的当前目录,但是可以通过设置来更改/proc/sys/kernel/core_pattern
  • 在某些情况下,中的内核值/proc/sys/fs/suid_dumpable可能会阻止生成内核。

手册页中介绍了更多可能会阻止生成的情况man core


9

为了激活核心转储,请执行以下操作:

  1. /etc/profile注释行中:

    # ulimit -S -c 0 > /dev/null 2>&1
  2. /etc/security/limits.conf注释行中:

    *               soft    core            0
  3. 执行cmd limit coredumpsize unlimited并使用cmd进行检查limit

    # limit coredumpsize unlimited
    # limit
    cputime      unlimited
    filesize     unlimited
    datasize     unlimited
    stacksize    10240 kbytes
    coredumpsize unlimited
    memoryuse    unlimited
    vmemoryuse   unlimited
    descriptors  1024
    memorylocked 32 kbytes
    maxproc      528383
    #
  4. 要检查是否写入了核心文件,您可以使用cmd终止相关进程kill -s SEGV <PID>(不需要,如果没有写入核心文件,可以将其用作检查):

    # kill -s SEGV <PID>

一旦写入了核心文件,请确保在相关文件(1./2./3。)中再次停用coredump设置!


9

对于Ubuntu 14.04

  1. 检查核心转储是否启用:

    ulimit -a
  2. 其中一行应为:

    core file size          (blocks, -c) unlimited
  3. 如果不 :

    gedit ~/.bashrc并添加ulimit -c unlimited到文件末尾并保存,然后重新运行终端。

  4. 使用调试信息构建应用程序:

    在Makefile中 -O0 -g

  5. 运行创建核心转储的应用程序(应在application_name文件附近创建名称为“ core”的核心转储文件):

    ./application_name
  6. 在gdb下运行:

    gdb application_name core

在第3步中,如何“重新运行”终端?你是说重启吗?
Naveen

@Naveen不,只需关闭终端并打开新终端,似乎您也可以将ulimit -c unlimited终端放在临时解决方案中,因为只有编辑~/.bashrc需要终端restrart才能生效。
mrgloom

4

默认情况下,您将获得一个核心文件。检查进程的当前目录是否可写,否则将不创建任何核心文件。


4
“进程的当前目录”是指运行进程时的$ cwd吗?〜/ abc> / usr / bin / cat def如果cat崩溃,当前的目录是有问题的〜/ abc还是/ usr / bin?
内森·费尔曼

5
〜/ abc。嗯,评论必须长15个字符!
马克·哈里森

5
这将是SEGV时的当前目录。同样,以与实际用户/组不同的有效用户和/或组运行的进程将不会写入核心文件。
达伦(Darron)2010年

2

最好使用系统调用以编程方式打开核心转储setrlimit

例:

#include <sys/resource.h>

bool enable_core_dump(){    
    struct rlimit corelim;

    corelim.rlim_cur = RLIM_INFINITY;
    corelim.rlim_max = RLIM_INFINITY;

    return (0 == setrlimit(RLIMIT_CORE, &corelim));
}

为什么这样更好?
内森·费尔曼

崩溃后生成的核心文件,无需ulimit -c unlimited在命令行环境中运行,然后重新运行应用程序。
kgbook '18

我不希望每次崩溃时都发生核心转储,只有当用户作为开发人员与我联系来查看它时,我才希望这样做。如果崩溃100次,则无需查看100个核心转储。
内森·费尔曼

在这种情况下,最好使用ulimit -c unlimited。您也可以使用marco定义进行编译,enable_core_dump如果发布时未定义该宏,则应用程序将不包含符号,并且您将获得核心转储替换为调试版本。
kgbook '18年

即使它由宏限定,如果我想生成核心转储,仍然需要我重新编译,而不是在重新运行之前简单地在shell中执行命令。
内森·费尔曼

1

值得一提的是,如果您设置了systemd,则情况会有所不同。该设置通常将通过core_patternsysctl值通过管道传输核心文件systemd-coredump(8)。核心文件大小rlimit通常已经配置为“无限制”。

然后可以使用检索核心转储coredumpctl(1)

核心转储的存储等由配置coredump.conf(5)。在coredumpctl手册页中有一些如何获取核心文件的示例,但总之,它看起来像这样:

查找核心文件:

[vps@phoenix]~$ coredumpctl list test_me | tail -1
Sun 2019-01-20 11:17:33 CET   16163  1224  1224  11 present /home/vps/test_me

获取核心文件:

[vps@phoenix]~$ coredumpctl -o test_me.core dump 16163

0

Ubuntu 19.04

所有其他答案本身并没有帮助我。但是以下总结完成了工作

创建~/.config/apport/settings具有以下内容:

[main]
unpackaged=true

(这告诉apport还为自定义应用编写核心转储)

检查:ulimit -c。如果输出0,请使用

ulimit -c unlimited

以防万一重启apport:

sudo systemctl restart apport

现在已将崩溃文件写入/var/crash/。但是您不能将它们与gdb一起使用。要将它们与gdb一起使用,请使用

apport-unpack <location_of_report> <target_directory>

更多信息:

  • 一些答案表明正在改变core_pattern。请注意,重新启动时,该文件可能会被apport服务覆盖。
  • 仅仅停止分摊并没有完成这项工作
  • ulimit -c在尝试其他网络答案时,该值可能会自动更改。在设置核心转储创建过程中,请确保定期检查它。

参考文献:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.