Linux中如何在进程出现分段错误时生成核心转储?
我在Linux中有一个进程出现分段错误的进程。 如何告诉它在失败时生成核心转储?
这取决于你正在使用的shell。 如果使用bash,则ulimit命令控制与程序执行相关的几个设置,例如是否应该转储核心。 如果你input
ulimit -c unlimited
那么这将告诉bash其程序可以转储任何大小的核心。 你可以指定一个大小,如52M,而不是无限的,但实际上这不应该是必要的,因为核心文件的大小可能永远不会是你的问题。
在tcsh中,你会input
limit coredumpsize unlimited
如上所述,这里提出的真正问题是如何在未启用系统的情况下启用核心转储。 这个问题在这里回答。
如果你来这里希望学习如何生成一个挂起的过程核心转储,答案是
gcore <pid>
如果你的系统上没有gcore的话
kill -ABRT <pid>
不要使用kill -SEGV,因为它经常会调用一个信号处理程序,从而难以诊断卡住的进程
我最后做的是在进程崩溃之前将gdb附加到进程中,然后在得到segfault时执行了generate-core-file
命令。 这迫使核心转储的产生。
也许你可以这样做,这个程序演示了如何将一个分段错误和shell捕获到一个debugging器(这是在AIX
下使用的原始代码),并将堆栈跟踪打印到分段故障点。 您将需要更改sprintf
variables以在Linux中使用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转储核心,如此处所示。
还有更多的事情可能会影响核心转储的产生。 我遇到这些:
- 转储的目录必须是可写的。 默认情况下,这是进程的当前目录,但可以通过设置
/proc/sys/kernel/core_pattern
。 - 在某些情况下,
/proc/sys/fs/suid_dumpable
的内核值可能会阻止生成内核。
有更多的情况可能会阻止在手册页中描述的世代 – 尝试man core
。
要检查核心转储的生成位置,请运行:
sysctl kernel.core_pattern
其中%e
是进程名称, %t
是系统时间。 您可以在/etc/sysctl.conf
更改它,并通过sysctl -p
重新加载。
如果核心文件没有生成(testing: sleep 10 &
和killall -SIGSEGV sleep
),检查限制: ulimit -a
。
如果核心文件大小有限,请运行:
ulimit -c unlimited
使其无限制。
然后再次testing,如果核心转储成功,则会在分段故障指示后看到“(核心转储)”,如下所示:
分割故障:11(核心转储)
Ubuntu的
在Ubuntu中,转储通常由/var/crash/
的apport
处理,但采用不同的格式,但在稳定版本中默认情况下不启用。 阅读更多在Ubuntu维基 。
它使用core_pattern
将核心转储直接传送到apport:
$ cat /proc/sys/kernel/core_pattern |/usr/share/apport/apport %p %s %c
所以即使核心文件被ulimit
禁用, apport
仍然会捕获崩溃( 如何启用或禁用Apport? )。
OS X
对于OS X,请参阅: 如何在Mac OS X中生成核心转储?
为了激活核心转储,请执行以下操作:
-
在
/etc/profile
注释行:# ulimit -S -c 0 > /dev/null 2>&1
-
在
/etc/security/limits.conf
注释掉这行:* soft core 0
-
执行cmd
limit coredumpsize unlimited
并使用cmdlimit
检查:# 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>
核心文件写入后,请确保在相关文件(1./2./3。)中再次取消核心转储设置!
默认情况下你会得到一个核心文件。 检查进程的当前目录是否可写,否则不会创build核心文件。
对于Ubuntu 14.04
-
检查核心转储已启用:
ulimit –a
-
其中一条线应该是:
core file size (blocks, -c) unlimited
-
如果不 :
gedit ~/.bashrc
并添加ulimit -c unlimited
到文件结尾并保存,重新运行terminal。 -
用debugging信息构build您的应用程序:
在Makefile
-O0 -g
-
运行创build核心转储的应用程序(在application_name文件附近应该创build名称为“core”的核心转储文件):
./application_name
-
在gdb下运行:
gdb application_name core