日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

如何在linux程序中捕获异常信号

發布時間:2023/12/15 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何在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、其他原因

  • 使用非法的指針,包括使用未經初始化及已經釋放的指針(指針使用之前和釋放之后置為NULL);
  • 內存讀/寫越界。包括數組訪問越界,或在使用一些寫內存的函數時,長度指定不正確或者這些函數本身不能指定長度,典型的函數有strcpy(strncpy),sprintf(snprint)等等;
  • 對于C++對象,請通過相應類的接口來去內存進行操作,禁止通過其返回的指針對內存進行寫操作,典型的如string類的data()和c_str()兩個接口;
  • 函數不要返回其中局部對象的引用或地址,當函數返回時,函數棧彈出,局部對象的地址將失效,改寫或讀這些地址都會造成未知的后果;
  • 避免在棧中定義過大的數組,否則可能導致進程的??臻g不足,此時也會出現段錯誤;
  • 操作系統的相關限制,如:進程可以分配的最大內存,進程可以打開的最大文件描述符個數等,這些需要通過ulimit或setrlimit或sysctl來解除相關的限制;
  • 多線程的程序,涉及到多個線程同時操作一塊內存時必須進行互斥,否則內存中的內存將不可預料;
  • 使用非線程安全的函數調用,例如strerror函數等;
  • 在有信號的環境中,使用不可重入函數調用,而這些函數內部會讀或寫某片內存區,當信號中斷時,內存寫操作將被打斷,而下次進入時將不避免的出錯;
  • 跨進程傳遞某個地址;
  • 某些有特殊要求的系統調用,例如epool_wait,正常情況下使用close關閉一個套接字后,epool會不再返回這個socket上的事件,但是如果你使用dup或dup2操作,將導致epool無法進行移除操作。
  • 三、怎么捕獲段錯誤信息

    可以通過向系統注冊一個段錯誤的捕獲函數,來實現段錯誤的捕獲。如下:

    /************************************************************************************************** ** 函數名稱: 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就將進程停止在那里。

    四、其他注意事項

  • 出現段錯誤時,首先應該想到段錯誤的定義,從它出發考慮引發錯誤的原因。
  • 在使用指針時,定義了指針后記得初始化指針,在使用的時候記得判斷是否為NULL。
  • 在使用數組時,注意數組是否被初始化,數組下標是否越界,數組元素是否存在等。
  • 在訪問變量時,注意變量所占地址空間是否已經被程序釋放掉。
  • 在處理變量時,注意變量的格式控制是否合理等。
  • 總結

    以上是生活随笔為你收集整理的如何在linux程序中捕获异常信号的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。