我有一个进程在Linux得到一个分割错误。当它失败时,我如何告诉它生成一个核心转储?
当前回答
也许您可以这样做,这个程序演示了如何捕获分段错误并将其外壳传递给调试器(这是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转储内核,如本文所示。
其他回答
为了激活核心转储,请执行以下操作:
In /etc/profile comment the line: # ulimit -S -c 0 > /dev/null 2>&1 In /etc/security/limits.conf comment out the line: * soft core 0 execute the cmd limit coredumpsize unlimited and check it with 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 # to check if the corefile gets written you can kill the relating process with cmd kill -s SEGV <PID> (should not be needed, just in case no core file gets written this can be used as a check): # kill -s SEGV <PID>
一旦corefile被写入,请确保在相关文件(1./2./3.)中再次禁用coredump设置!
如上所述,这里提出的真正问题是如何在未启用核心转储的系统上启用它们。这个问题在这里得到了解答。
如果您希望了解如何为挂起进程生成核心转储,那么答案是
gcore <pid>
如果gcore在您的系统上不可用,那么
kill -ABRT <pid>
不要使用kill -SEGV,因为它经常会调用信号处理程序,使诊断卡住进程更加困难
这取决于您使用的shell。如果使用bash,则ulimit命令控制与程序执行相关的几个设置,例如是否应该转储core。如果你输入
ulimit -c unlimited
然后这将告诉bash,它的程序可以转储任何大小的内核。如果你愿意,你可以指定一个大小,比如52M,而不是无限,但在实践中,这不应该是必要的,因为核心文件的大小可能永远不会成为你的问题。
在tcsh中,您需要输入
limit coredumpsize unlimited
还有更多的事情可能会影响核心转储的生成。我遇到了这些问题:
转储目录必须是可写的。默认情况下,这是进程的当前目录,但是可以通过设置/proc/sys/kernel/core_pattern来改变。 在某些情况下,/proc/sys/fs/suid_dumpable中的内核值可能会阻止内核的生成。
还有更多的情况可能会阻止手册页中描述的生成- try man核心。
默认情况下,您将得到一个核心文件。检查进程的当前目录是否可写,否则不会创建核心文件。