linux下程序對SIGSEGV信號的默認處理方式是產生coredump並終止程序,可以參考man 7 signal
Signal Value Action Comment ────────────────────────────────────────────────────────────────────── SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Cont Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at terminal SIGTTIN 21,21,26 Stop Terminal input for background process SIGTTOU 22,22,27 Stop Terminal output for background process
對於Action的描述
The entries in the "Action" column of the tables below specify the default disposition for each signal, as follows: Term Default action is to terminate the process. Ign Default action is to ignore the signal. Core Default action is to terminate the process and dump core (see core(5)). Stop Default action is to stop the process. Cont Default action is to continue the process if it is currently stopped.
可以看到產生core這個動作的信號不止SIGSEGV這一個。通常程序中有對內存的Invalid reference就會產生SIGSEGV,具體描述見http://www.cnblogs.com/thammer/p/4737371.html 。
分析段錯誤的方法:
1.直接使用gdb
如果是容易重現的SIGSEGV直接gdb掛着運行,產生SIGSEGV時gdb會停止並打印提示,這時直接敲入命令bt查看程序此時的函數調用棧幀就可以定位到是哪個函數在什么樣的調用情況下出現段錯誤。
2.使用core文件+gdb
在程序收到SIGSEGV時會產生coredump,core文件就是異常進程在發生異常的那一個時刻的進程內存上下文和cpu寄存器的信息。
首先,設置core文件大小 ulimit -c XXXX,XXXX就是允許產生的core文件大小,通常設置為unlimited,不限定大小
然后,運行程序直至產生core文件,名字一般是core.xxx,xxx為程序進程號,不同發行版本可能有不同的命名規則
然后,運行gdb,敲入命令 core-file corefile-name,再bt即可
3.注冊SIGSEGV信號處理函數,在處理函數里面使用一些堆棧回溯的函數打印棧幀信息。
A.使用glibc帶的函數backtrace backtrace_symbols backtrace_symbols_fd打印
void SigSegv_handler(int signo) { int j, nptrs; void *buffer[BT_BUF_SIZE]; char **strings; nptrs = backtrace(buffer, BT_BUF_SIZE); printf("backtrace() returned %d addresses\n", nptrs); /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf("%s\n", strings[j]); free(strings);
exit(-1); }
backtrace_symbols 和backtrace_symbols_fd不同在於后者將打印輸入到一個fd指定的文件里面。
它有一定的限制:
These functions make some assumptions about how a function's return address is stored on the stack. Note the following: * Omission of the frame pointers (as implied by any of gcc(1)'s nonzero optimization levels) may cause these assumptions to be violated. * Inlined functions do not have stack frames. * Tail-call optimization causes one stack frame to replace another. The symbol names may be unavailable without the use of special linker options. For systems using the GNU linker, it is necessary to use the -rdynamic linker option. Note that names of "static" functions are not exposed, and won't be available in the backtrace.
對優化的程序可能失效
對inline函數失效
對static函數僅能打印函數地址
對tail-call優化的函數失效
編譯時需要加入 -rdynamic
B.還有其他方法或接口做類似backtrace的事情,以后補充