Segmentation Fault 由 CPU 檢測到非法內存訪問觸發,內核向進程發送 SIGSEGV 信號。默認處理:終止進程、輸出「段錯誤」、生成 Core 文件(若啟用)。
// segfault_demo.c
void dummy_function(void) {
unsigned char* ptr = (unsigned char*)0x00;
*ptr = 0x00; // 寫入地址 0 → 必然段錯
}
int main(void) {
dummy_function();
return 0;
}
$ gcc -g -rdynamic segfault_demo.c -o demo
$ gdb ./demo
(gdb) run
Starting program: /path/demo
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555149 in dummy_function () at segfault_demo.c:4
4 *ptr = 0x00;
(gdb) bt # 查看調用棧
#0 dummy_function () at segfault_demo.c:4
#1 0x000055555555515a in main () at segfault_demo.c:9
優點:直觀、支持斷點/單步/變量檢查
缺點:需重現問題、調試版性能較差、生產環境難用
$ ulimit -c unlimited # 當前會話啟用
# 或永久:/etc/security/limits.conf 添加
* soft core unlimited
* hard core unlimited
$ ./demo
Segmentation fault (core dumped)
$ gdb ./demo core
Core was generated by `./demo'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000555555555149 in dummy_function () at segfault_demo.c:4
4 *ptr = 0x00;
優點:無需重現、線上環境直接用、保留崩潰現場
關鍵設置:編譯加 -g,部署保留符號表或單獨存儲 .debug 文件
// auto_gdb.c
#include
#include
#include
#include
#include
void segv_handler(int signo) {
char cmd[1024];
// 獲取當前進程可執行文件路徑
char exe_path[1024];
ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path)-1);
if (len <= 0) exit(1);
exe_path[len] = '\0';
// 構造 gdb 命令:附加到當前進程
snprintf(cmd, sizeof(cmd), "gdb -p %d -ex 'bt' -ex 'quit'", getpid());
system(cmd); // 彈出 GDB 會話
exit(1);
}
int main() {
signal(SIGSEGV, segv_handler);
dummy_function(); // 觸發段錯
return 0;
}
效果:程序崩潰瞬間自動彈出 GDB 顯示回溯,適合測試環境自動化捕獲。
// backtrace_demo.c
#include
#include
#include
#include
void bt_handler(int signo) {
void* array[32];
size_t size = backtrace(array, 32);
char** strings = backtrace_symbols(array, size);
printf("=== Stack Trace (%zu frames) ===\n", size);
for (size_t i = 0; i < size; i++) {
printf("%s\n", strings[i]);
}
free(strings);
exit(1);
}
int main() {
signal(SIGSEGV, bt_handler);
dummy_function();
return 0;
}
$ gcc -g -rdynamic backtrace_demo.c -o bt_demo
$ ./bt_demo
=== Stack Trace (5 frames) ===
./bt_demo(bt_handler+0x19) [0x401126]
[0x7ffff7fd8420] // 信號幀
./bt_demo(main+0x35) [0x4011d5]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe6) [0x7ffff7dea866]
./bt_demo(_start+0x2a) [0x40105a]
$ objdump -d bt_demo | grep -A5 "4011d5"
00000000004011d5 :
4011d5: e8 2a ff ff ff call 401104
4011da: b8 00 00 00 00 mov $0x0,%eax
確認 main+0x35 對應 call dummy_function,即源碼調用處。
| 工具/技術 | 適用場景 | 關鍵命令 |
|---|---|---|
| AddressSanitizer (ASan) | 開發期捕獲內存錯誤 | gcc -fsanitize=address -g |
| Valgrind Memcheck | 全面內存錯誤檢測 | valgrind --tool=memcheck ./prog |
gdb watch | 監視變量變化 | watch *ptr |
coredumpctl | systemd 管理 core 文件 | coredumpctl debug |
| rr (Record and Replay) | 確定性重現並行 Bug | rr record ./prog; rr replay |
-g:無調試信息無法定位行號coredumpctl 統一管理objcopy --only-keep-debug 分離調試信息man 7 signal:理解各信號默認行為與可捕獲性| 代碼模式 | 原因 | 檢查重點 |
|---|---|---|
*ptr = x (ptr=NULL/0) | 空指針解引用 | malloc 返回值檢查、參數合法性 |
arr[i] = x (i≥size) | 數組越界 | 循環邊界、輸入驗證 |
free(ptr); ... *ptr | Use-after-free | 指針置 NULL、所有權管理 |
free(ptr); free(ptr) | Double free | 同前 |
| 棧溢出(大局部數組/無限遞歸) | 棧空間耗盡 | 改用堆分配、遞歸轉迭代 |
| 字符串字面量修改 | 寫只讀段 | char s[] = "..." 而非 char* s = "..." |
段錯誤看似可怕,掌握這四大定位術配合現代檢測工具,多數情況下「一眼定位、秒級修復」。建議將 backtrace 方案集成到項目公共錯誤處理庫中,構建「零成本事後調試能力」。