如何在linux程序中捕获异常信号
最近在搞一個linux項目,碰巧遇到了一個段錯誤的問題。經過一段時間的排查和學習,對段錯誤這個概念有了些許的理解,現總結如下:
一、什么是段錯誤
一句話來說,段錯誤是指訪問的內存超出了系統給這個程序所設定的內存空間,例如訪問了不存在的內存地址、訪問了系統保護的內存地址、訪問了只讀的內存地址等等情況。這里貼一個對于“段錯誤”的準確定義:
A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.
Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, but much of the terminology of segmentation is still used, “segmentation fault” being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.
On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.
二、為什么會出現段錯誤
很多非法的操作都會導致系統產生段錯誤,大致分為以下幾類:
1、訪問不存在的內存地址
#include<stdio.h> #include<stdlib.h> void main() {int *ptr = NULL;*ptr = 0; }2、訪問系統保護的內存地址
#include<stdio.h> #include<stdlib.h> void main() {int *ptr = (int *)0;*ptr = 100; }3、訪問只讀的內存地址
#include<stdio.h> #include<stdlib.h> #include<string.h> void main() {char *ptr = "test";strcpy(ptr, "TEST"); }4、棧溢出
#include<stdio.h> #include<stdlib.h> void main() {main(); }5、其他原因
三、怎么捕獲段錯誤信息
可以通過向系統注冊一個段錯誤的捕獲函數,來實現段錯誤的捕獲。如下:
/************************************************************************************************** ** 函數名稱: segv_error_handle ** 功能描述: 段錯誤的實際處理函數 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ static void segv_error_handle(int v) {system("sync");printf("segv_error(value: %d), proc is going to exit now!!!\n", v);exit(1); }/************************************************************************************************** ** 函數名稱: install_segv_handler ** 功能描述: 注冊段錯誤的處理函數 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ static void install_segv_handler() {struct sigaction siga;siga.sa_handler = segv_error_handle;siga.sa_flags = 0;memset(&siga.sa_mask, 0, sizeof(sigset_t));sigaction(SIGSEGV, &siga, NULL); /* 捕獲段非法錯誤的信號 */sigaction(SIGTERM, &siga, NULL); /* 捕獲軟件終止的信號 */sigaction(SIGINT, &siga, NULL); /* 捕獲進程中斷的信號 */ }/************************************************************************************************** ** 函數名稱: main ** 功能描述: 主函數 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ int main(int argc, char *argv[]) {install_segv_handler(); /* 段錯誤的處理函數 */........ /* 其他代碼 */ }可以看到,上述代碼中,向系統注冊了3類的段錯誤,分別為SIGSEGV,SIGTERM,SIGINT。那么,這幾個類型到底代表什么意思?除了他們之外,還有哪些其他的類型呢?參見下表:
01)SIGHUP:本信號在用戶終端連接(正?;蚍钦?#xff09;結束時發出,通常是在終端的控制進程結束時,通知同一session內的各個作業,這時它們與控制終端不再關聯;
02)SIGINT:程序終止(interrupt)信號,在用戶鍵入INTR字符(通常是Ctrl-C)時發出;
03)SIGQUIT:和SIGINT類似,但由QUIT字符(通常是Ctrl-)來控制。進程在因收到SIGQUIT退出時會產生core文件,在這個意義上類似于一個程序錯誤信號;
04)SIGILL:執行了非法指令。通常是因為可執行文件本身出現錯誤,或者試圖執行數據段。堆棧溢出時也有可能產生這個信號;
05)SIGTRAP:由斷點指令或其它trap指令產生,由debugger使用;
06)SIGABRT:程序自己發現錯誤并調用abort時產生;
06)SIGIOT:在PDP-11上由iot指令產生,在其它機器上和SIGABRT一樣;
07)SIGBUS:非法地址,:包括內存地址對齊(alignment)出錯。eg:訪問一個四個字長的整數,但其地址不是4的倍數;
08)SIGFPE:在發生致命的算術運算錯誤時發出。不僅包括浮點運算錯誤,還包括溢出及除數為0等其它所有的算術的錯誤;
09)SIGKILL:用來立即結束程序的運行。本信號不能被阻塞,處理和忽略;
10)SIGUSR1:留給用戶使用;
11)SIGSEGV:試圖訪問未分配給自己的內存,或試圖往沒有寫權限的內存地址寫數據;
12)SIGUSR2:留給用戶使用;
13)SIGPIPE:Broken:pipe;
14)SIGALRM:時鐘定時信號,計算的是實際的時間或時鐘時間。alarm函數使用該信號;
15)SIGTERM:程序結束(terminate)信號,與SIGKILL不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出。shell命令kill缺省產生這個信號;
17)SIGCHLD:子進程結束時,父進程會收到這個信號;
18)SIGCONT:讓一個停止(stopped)的進程繼續執行。本信號不能被阻塞??梢杂靡粋€handler來讓程序在由stopped狀態變為繼續執行時完成特定的工作。例如,重新顯示提示符;
19)SIGSTOP:停止(stopped)進程的執行。注意它和terminate以及interrupt的區別:該進程還未結束,只是暫停執行。本信號不能被阻塞,處理或忽略;
20)SIGTSTP:停止進程的運行,但該信號可以被處理和忽略。用戶鍵入SUSP字符時(通常是Ctrl-Z)發出這個信號;
21)SIGTTIN:當后臺作業要從用戶終端讀數據時,該作業中的所有進程會收到SIGTTIN信號。缺省時這些進程會停止執行;
22)SIGTTOU:類似于SIGTTIN,但在寫終端(或修改終端模式)時收到;
23)SIGURG:有”緊急”數據或out-of-band數據到達socket時產生;
24)SIGXCPU:超過CPU時間資源限制。這個限制可以由getrlimit/setrlimit來讀取/改變;
25)SIGXFSZ:超過文件大小資源限制;
26)SIGVTALRM:虛擬時鐘信號。類似于SIGALRM,但是計算的是該進程占用的CPU時間;
27)SIGPROF:類似于SIGALRM/SIGVTALRM,但包括該進程用的CPU時間以及系統調用的時間;
28)SIGWINCH:窗口大小改變時發出;
29)SIGIO:文件描述符準備就緒,可以開始進行輸入/輸出操作;
30)SIGPWR:Power:failure;
其中有兩個信號可以停止進程:SIGTERM和SIGKILL。
- SIGTERM比較友好,進程能捕捉這個信號,根據您的需要來關閉程序。在關閉程序之前,您可以結束打開的記錄文件和完成正在做的任務。在某些情況下,假如進程正在進行作業而且不能中斷,那么進程可以忽略這個SIGTERM信號。
- 對于SIGKILL信號,進程是不能忽略的。這是一個“我不管您在做什么,立刻停止”的信號。假如發送SIGKILL信號給進程,linux就將進程停止在那里。
四、其他注意事項
總結
以上是生活随笔為你收集整理的如何在linux程序中捕获异常信号的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据挖掘面试 150 道题(附答案)(常
- 下一篇: linux内核驱动模块开发步骤及实例入门