首页 文章

当进程出现分段错误时,如何在Linux中生成核心转储?

提问于
浏览
185

我在Linux中有一个进程出现分段错误的进程 . 如何在失败时告诉它生成核心转储?

10 回答

  • 24

    这取决于您使用的是什么shell . 如果使用bash,则ulimit命令控制与程序执行相关的几个设置,例如是否应转储核心 . 如果你输入

    ulimit -c unlimited
    

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

    在tcsh中,你要输入

    limit coredumpsize unlimited
    
  • 4

    如上所述,这里要问的真正问题是如何在未启用核心转储的系统上启用核心转储 . 这个问题在这里得到解答 .

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

    gcore <pid>
    

    如果你的系统上没有gcore那么

    kill -ABRT <pid>
    

    不要使用kill -SEGV,因为它通常会调用信号处理程序,使得更难诊断卡住的进程

  • 15

    我最后做的是在崩溃之前将gdb附加到进程,然后当它得到段错误时我执行了 generate-core-file 命令 . 这迫使生成核心转储 .

  • 8

    也许你可以这样做,这个程序演示了如何捕获分段错误并弹出调试器(这是 AIX 下使用的原始代码),并将堆栈跟踪打印到分段故障点 . 在Linux的情况下,您需要更改 sprintf 变量以使用 gdb .

    #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转储核心,如此博客here中所示 .

  • 218

    要检查核心转储的生成位置,请运行:

    sysctl kernel.core_pattern
    

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

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

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

    ulimit -c unlimited
    

    让它无限 .

    然后再次测试,如果核心转储成功,您将在分段故障指示后看到“(core dumped)”,如下所示:

    分段故障:11(核心转储)


    Ubuntu

    在Ubuntu中,转储通常由 killall -SIGSEGV sleep /var/crash/ 处理,但格式不同,但默认情况下不会在稳定版本中启用 . 阅读更多内容,请致电Ubuntu wiki .

    它使用 core_pattern 直接将核心转储管道传输到apport:

    $ cat /proc/sys/kernel/core_pattern
    |/usr/share/apport/apport %p %s %c
    

    因此,即使核心文件被 ulimit 禁用, apport 仍将捕获崩溃(How do I enable or disable Apport?) .


    macOS

    对于macOS,请参阅:How to generate core dumps in Mac OS X?

  • 50

    有更多的事情可能会影响核心转储的产生 . 我遇到了这些:

    • 转储目录必须是可写的 . 默认情况下,这是进程的当前目录,但可以通过设置 /proc/sys/kernel/core_pattern 来更改 .

    • 在某些情况下, /proc/sys/fs/suid_dumpable 中的内核值可能会阻止生成核心 .

    有更多情况可能会阻止手册页中描述的生成 - 尝试 man core .

  • 19

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

    • /etc/profile 评论该行:
    # ulimit -S -c 0 > /dev/null 2>&1
    
    • /etc/security/limits.conf 注释掉了这一行:
    *               soft    core            0
    
    • 执行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
    #
    
    • 检查核心文件是否被写入你可以用cmd kill -s SEGV <PID> 杀死相关进程(不应该需要,以防万一没有核心文件写入,这可以用作检查):
    # kill -s SEGV <PID>
    

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

  • 0

    对于Ubuntu 14.04

    • 检查核心转储是否已启用:
    ulimit -a
    
    • 其中一行应该是:
    core file size          (blocks, -c) unlimited
    
    • 如果不是:

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

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

    在Makefile中 -O0 -g

    • 运行创建核心转储的应用程序(应在application_name文件附近创建名为'core'的核心转储文件):
    ./application_name
    
    • 在gdb下运行:
    gdb application_name core
    
  • 19

    默认情况下,您将获得一个核心文件 . 检查进程的当前目录是否可写,或者没有核心文件创建 .

  • 8

    最好使用系统调用 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));
    }
    

相关问题