自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳談 UNIX 環(huán)境進(jìn)程異常退出

系統(tǒng)
本文詳細(xì)論述UNIX環(huán)境上的進(jìn)程異常退出,將導(dǎo)致進(jìn)程異常退出的各種情景歸納為兩類,對每類情況詳細(xì)分析了問題出現(xiàn)的根本原因,同時添加了相應(yīng)的實(shí)例以易于您更好地進(jìn)行了解。

本文詳細(xì)論述UNIX環(huán)境上的進(jìn)程異常退出,將導(dǎo)致進(jìn)程異常退出的各種情景歸納為兩類,對每類情況詳細(xì)分析了問題出現(xiàn)的根本原因,同時添加了相應(yīng)的實(shí)例以易于您更好地進(jìn)行了解。在此基礎(chǔ)上,文章最后論述了應(yīng)該如何避免和調(diào)試進(jìn)程異常退出問題。希望讀者閱讀此文后,對進(jìn)程異常退出問題有更深層的認(rèn)識,有更系統(tǒng)的梳理,對調(diào)試此類進(jìn)程崩潰問題時也能有所幫助,寫出更穩(wěn)定、更可靠的軟件。

進(jìn)程異常退出

進(jìn)程退出意味著進(jìn)程生命期的結(jié)束,系統(tǒng)資源被回收,進(jìn)程從操作系統(tǒng)環(huán)境中銷毀。進(jìn)程異常退出是進(jìn)程在運(yùn)行過程中被意外終止,從而導(dǎo)致進(jìn)程本來應(yīng)該繼續(xù)執(zhí)行的任務(wù)無法完成。進(jìn)程異常退出可能給軟件用戶造成如下負(fù)面影響:

  1. 軟件喪失部分或者全部功能性,無法完成既定任務(wù)。
  2. 如果進(jìn)程正在處理數(shù)據(jù),可能造成數(shù)據(jù)損壞。
  3. 如果是關(guān)鍵軟件服務(wù),必然導(dǎo)致服務(wù)異常中止,造成無法預(yù)計(jì)的損失。
  4. 進(jìn)程異常退出或者進(jìn)程崩潰,也會給軟件用戶造成恐慌和困惑。

進(jìn)程異常退出是生產(chǎn)環(huán)境中經(jīng)常遇到的問題,它會給軟件用戶造成很多負(fù)面影響,所以軟件開發(fā)者應(yīng)當(dāng)避免這種問題的出現(xiàn)。但是導(dǎo)致進(jìn)程異常退出的場景和原因是多種多樣的,甚至令人琢磨不透。

本文將所有可能造成進(jìn)程異常退出的原因歸結(jié)為兩類。系統(tǒng)地將其分類,使讀者對此類問題能有清晰的認(rèn)識。對每類情況詳細(xì)論述,分析根本原因,然后分析了這兩類情況之間的聯(lián)系,也就是信號與進(jìn)程異常退出的緊密關(guān)系。希望您讀完此文后,能對此類問題有更加全面、深入的理解,對調(diào)試此類問題也能有所幫助,寫出更加可靠、更加穩(wěn)定性、更加健壯的軟件。

首先我們來看導(dǎo)致進(jìn)程異常退出的這兩類情況:

第一類:向進(jìn)程發(fā)送信號導(dǎo)致進(jìn)程異常退出;

第二類:代碼錯誤導(dǎo)致進(jìn)程運(yùn)行時異常退出。

第一類:向進(jìn)程發(fā)送信號導(dǎo)致進(jìn)程異常退出

信號:

UNIX系統(tǒng)中的信號是系統(tǒng)響應(yīng)某些狀況而產(chǎn)生的事件,是進(jìn)程間通信的一種方式。信號可以由一個進(jìn)程發(fā)送給另外進(jìn)程,也可以由核發(fā)送給進(jìn)程。

信號處理程序:

信號處理程序是進(jìn)程在接收到信號后,系統(tǒng)對信號的響應(yīng)。根據(jù)具體信號的涵義,相應(yīng)的默認(rèn)信號處理程序會采取不同的信號處理方式:

  • 終止進(jìn)程運(yùn)行,并且產(chǎn)生core dump文件。
  • 終止進(jìn)程運(yùn)行。
  • 忽略信號,進(jìn)程繼續(xù)執(zhí)行。
  • 暫停進(jìn)程運(yùn)行。
  • 如果進(jìn)程已被暫停,重新調(diào)度進(jìn)程繼續(xù)執(zhí)行。

前兩種方式會導(dǎo)致進(jìn)程異常退出,是本文討論的范圍。實(shí)際上,大多數(shù)默認(rèn)信號處理程序都會終止進(jìn)程的運(yùn)行。

在進(jìn)程接收到信號后,如果進(jìn)程已經(jīng)綁定自定義的信號處理程序,進(jìn)程會在用戶態(tài)執(zhí)行自定義的信號處理程序;反之,內(nèi)核會執(zhí)行默認(rèn)信號程序終止進(jìn)程運(yùn)行,導(dǎo)致進(jìn)程異常退出。

圖1.默認(rèn)信號處理程序終止進(jìn)程運(yùn)行

圖 1. 默認(rèn)信號處理程序終止進(jìn)程運(yùn)行

所以,通過向進(jìn)程發(fā)送信號可以觸發(fā)默認(rèn)信號處理程序,默認(rèn)信號處理程序終止進(jìn)程運(yùn)行。在UNIX環(huán)境中我們有三種方式將信號發(fā)送給目標(biāo)進(jìn)程,導(dǎo)致進(jìn)程異常退出。

方式一:調(diào)用函數(shù)kill()發(fā)送信號

我們可以調(diào)用函數(shù)kill(pid_t pid, int sig)向進(jìn)程ID為pid的進(jìn)程發(fā)送信號sig。這個函數(shù)的原型是:

  1. #include <sys/types.h>   
  2. #include <signal.h>   
  3. int kill(pid_t pid, int sig);  

調(diào)用函數(shù)kill()后,進(jìn)程進(jìn)入內(nèi)核態(tài)向目標(biāo)進(jìn)程發(fā)送指定信號;目標(biāo)進(jìn)程在接收到信號后,默認(rèn)信號處理程序被調(diào)用,進(jìn)程異常退出。

清單1.調(diào)用 kill() 函數(shù)發(fā)送信號

  1. /* sendSignal.c, send the signal ‘ SIGSEGV ’ to specific process*/   
  2.       1 #include <sys/types.h>   
  3.       2 #include <signal.h>   
  4.       3   
  5.       4 int main(int argc, char* argv[])   
  6.       5 {   
  7.       6     char* pid = argv[1];   
  8.       7     int PID = atoi(pid);   
  9.       8   
  10.       9     kill(PID, SIGSEGV);   
  11.      10     return 0;   
  12.      11 }  

上面的代碼片段演示了如何調(diào)用kill()函數(shù)向指定進(jìn)程發(fā)送SIGSEGV信號。編譯并且運(yùn)行程序:

  1. [root@machine ~]# gcc -o sendSignal sendSignal.c   
  2. [root@machine ~]# top &   
  3. [1] 22055   
  4. [root@machine ~]# ./sendSignal 22055   
  5. [1]+  Stopped                 top   
  6. [root@machine ~]# fg %1   
  7. top   
  8. Segmentation fault (core dumped)  

上面的操作中,我們在后臺運(yùn)行top,進(jìn)程ID是22055,然后運(yùn)行sendSignal向它發(fā)送SIGSEGV信號,導(dǎo)致top進(jìn)程異常退出,產(chǎn)生core dump文件。

方式二:運(yùn)行kill命令發(fā)送信號

用戶可以在命令模式下運(yùn)行kill命令向目標(biāo)進(jìn)程發(fā)送信號,格式為:

kill SIG*** PID

在運(yùn)行 kill 命令發(fā)送信號后,目標(biāo)進(jìn)程會異常退出。這也是系統(tǒng)管理員終結(jié)某個進(jìn)程的最常用方法,類似于在 Windows 平臺通過任務(wù)管理器殺死某個進(jìn)程。

在實(shí)現(xiàn)上,kill命令也是調(diào)用kill系統(tǒng)調(diào)用函數(shù)來發(fā)送信號。所以本質(zhì)上,方式一和方式二是一樣的。

操作演示如下:

  1. [root@machine ~]# top &   
  2. [1] 22810   
  3. [root@machine ~]# kill -SIGSEGV 22810   
  4. [1]+  Stopped                 top   
  5. [root@machine ~]# fg %1   
  6. top   
  7. Segmentation fault (core dumped)  

方式三:在終端使用鍵盤發(fā)送信號

用戶還可以在終端用鍵盤輸入特定的字符(比如control-C或control-\)向前臺進(jìn)程發(fā)送信號,終止前臺進(jìn)程運(yùn)行。常見的中斷字符組合是,使用control-C發(fā)送SIGINT信號,使用control-\ 發(fā)送SIGQUIT信號,使用control-z發(fā)送SIGTSTP信號。

在實(shí)現(xiàn)上,當(dāng)用戶輸入中斷字符組合時,比如control-C,終端驅(qū)動程序響應(yīng)鍵盤輸入,并且識別control-C是信號SIGINT的產(chǎn)生符號,然后向前臺進(jìn)程發(fā)送SIGINT信號。當(dāng)前臺進(jìn)程再次被調(diào)用時就會接收到SIGINT信號。

使用鍵盤中斷組合符號發(fā)送信號演示如下:

  1. [root@machine ~]# ./loop.sh  ( 注釋:運(yùn)行一個前臺進(jìn)程,任務(wù)是每秒鐘打印一次字符串 )   
  2. i'm looping ...   
  3. i'm looping ...   
  4. i'm looping ...                 ( 注釋:此時,用戶輸入 control-C)   
  5. [root@machine ~]#               ( 注釋:接收到信號后,進(jìn)程退出 )  

對這類情況的思考

這類情況導(dǎo)致的進(jìn)程異常退出,并不是軟件編程錯誤所導(dǎo)致,而是進(jìn)程外部的異步信號所致。但是我們可以在代碼編寫中做的更好,通過調(diào)用signal函數(shù)綁定信號處理程序來應(yīng)對信號的到來,以提高軟件的健壯性。

signal函數(shù)的原型:

  1. #include <signal.h>   
  2. void (*signal(int sig, void (*func)(int)))(int);  

signal函數(shù)將信號sig和自定義信號處理程序綁定,即當(dāng)進(jìn)程收到信號sig時自定義函數(shù)func被調(diào)用。如果我們希望軟件在運(yùn)行時屏蔽某個信號,插入下面的代碼,以達(dá)到屏蔽信號 SIGINT的效果:

  1. (void)signal(SIGINT, SIG_IGN); 

執(zhí)行這一行代碼后,當(dāng)進(jìn)程收到信號SIGINT后,進(jìn)程就不會異常退出,而是會忽視這個信號繼續(xù)運(yùn)行。

更重要的場景是,進(jìn)程在運(yùn)行過程中可能會創(chuàng)建一些臨時文件,我們希望進(jìn)程在清理這些文件后再退出,避免遺留垃圾文件,這種情況下我們也可以調(diào)用signal函數(shù)實(shí)現(xiàn),自定義一個信號處理程序來清理臨時文件,當(dāng)外部發(fā)送信號要求進(jìn)程終止運(yùn)行時,這個自定義信號處理程序被調(diào)用做清理工作。代碼清單2是具體實(shí)現(xiàn)。

清單2.調(diào)用signal函數(shù)綁定自定義信號處理程序

  1.                   
  2.       /*  bindSignal.c  */   
  3.       1 #include <signal.h>   
  4.       2 #include <stdio.h>   
  5.       3 #include <unistd.h>   
  6.       4 void cleanTask(int sig) {   
  7.       5     printf( "Got the signal, deleting the tmp file\n" );   
  8.       6     if( access( "/tmp/temp.lock", F_OK ) != -1 ) {   
  9.       7           if( remove( "/tmp/temp.lock" ) != 0 )   
  10.       8               perror( "Error deleting file" );   
  11.       9           else   
  12.      10               printf( "File successfully deleted\n" );   
  13.      11     }   
  14.      12   
  15.      13     printf( "Process existing...\n" );   
  16.      14     exit(0);   
  17.      15 }   
  18.      16   
  19.      17 int main() {   
  20.      18     (void) signal( SIGINT, cleanTask );   
  21.      19     FILE* tmp = fopen ( "/tmp/temp.lock", "w" );   
  22.      20     while(1) {   
  23.      21         printf( "Process running happily\n" );   
  24.      22         sleep(1);   
  25.      23     }   
  26.      24   
  27.      25     if( tmp )   
  28.      26         remove( "/tmp/temp.lock" );   
  29.      27 }   
  30. 運(yùn)行程序:  
  31.  [root@machine ~]# ./bindSignal   
  32.  Process running happily   
  33.  Process running happily   
  34.  Process running happily                       ( 注釋:此時,用戶輸入 control-C)   
  35.  Got the signal, deleting the tmp file      ( 注釋:接收到信號后,cleanTask 被調(diào)用 )   
  36.  File successfully deleted                    ( 注釋:cleanTask 刪除臨時文件 )   
  37.  Process existing...                           ( 注釋:進(jìn)程退出 )   

第二類:編程錯誤導(dǎo)致進(jìn)程運(yùn)行時異常退出

相比于第一類情況,第二類情況在軟件開發(fā)過程中是??停蔷幊体e誤,進(jìn)程運(yùn)行過程中非法操作引起的。

操作系統(tǒng)和計(jì)算機(jī)硬件為應(yīng)用程序的運(yùn)行提供了硬件平臺和軟件支持,為應(yīng)用程序提供了平臺虛擬化,使進(jìn)程運(yùn)行在自己的進(jìn)程空間。在進(jìn)程看來,它自身獨(dú)占整臺系統(tǒng),任何其它進(jìn)程都無法干預(yù),也無法進(jìn)入它的進(jìn)程空間。

但是操作系統(tǒng)和計(jì)算機(jī)硬件又約束每個進(jìn)程的行為,使進(jìn)程運(yùn)行在用戶態(tài)空間,控制權(quán)限,確保進(jìn)程不會破壞系統(tǒng)資源,不會干涉進(jìn)入其它進(jìn)程的空間,確保進(jìn)程合法訪問內(nèi)存。當(dāng)進(jìn)程嘗試突破禁區(qū)做非法操作時,系統(tǒng)會立刻覺察,并且終止進(jìn)程運(yùn)行。

所以,第二類情況導(dǎo)致的進(jìn)程異常退出,起源于進(jìn)程自身的編程錯誤,錯誤的編碼執(zhí)行非法操作,操作系統(tǒng)和硬件制止它的非法操作,并且讓進(jìn)程異常退出。

在實(shí)現(xiàn)上,操作系統(tǒng)和計(jì)算機(jī)硬件通過異常和異常處理函數(shù)來阻止進(jìn)程做非法操作。

異常和異常處理函數(shù)

當(dāng)進(jìn)程執(zhí)行非法操作時,計(jì)算機(jī)會拋出處理器異常,系統(tǒng)執(zhí)行異常處理函數(shù)以響應(yīng)處理器異常,異常處理函數(shù)往往會終止進(jìn)程運(yùn)行。

廣義的異常包括軟中斷(soft interrupts)和外設(shè)中斷(I/O interrupts)。外設(shè)中斷是系統(tǒng)外圍設(shè)備發(fā)送給處理器的中斷,它通知處理器I/O操作的狀態(tài),這種異常是外設(shè)的異步異常,與具體進(jìn)程無關(guān),所以它們不會造成進(jìn)程的異常退出。本文討論的異常是指soft interrupts,是進(jìn)程非法操作所導(dǎo)致的處理器異常,這類異常是進(jìn)程執(zhí)行非法操作所產(chǎn)生的同步異常,比如內(nèi)存保護(hù)異常,除 0 異常,缺頁異常等等。

處理器異常有很多種,系統(tǒng)為每個異常分配異常號,每個異常有相對應(yīng)的異常處理函數(shù)。以x86處理器為例,除0操作產(chǎn)生DEE異常(Divide Error Exception),異常號是0;內(nèi)存非法訪問產(chǎn)生GPF異常(General Protection Fault),異常號是13,而缺頁(page fault)異常的異常號是14。當(dāng)異常出現(xiàn)時,處理器掛起當(dāng)前進(jìn)程,讀取異常號,然后執(zhí)行相應(yīng)的異常處理函數(shù)。如果異常是可修復(fù),比如內(nèi)存缺頁異常,異常處理函數(shù)會修復(fù)系統(tǒng)錯誤狀態(tài),清除異常,然后重新執(zhí)行一遍被中斷的指令,進(jìn)程繼續(xù)運(yùn)行;如果異常無法修復(fù),比如內(nèi)存非法訪問或者除0操作,異常處理函數(shù)會終止進(jìn)程運(yùn)行,如圖2:

圖 2. 異常處理函數(shù)終止進(jìn)程運(yùn)行

圖 2. 異常處理函數(shù)終止進(jìn)程運(yùn)行#p#

實(shí)例以及分析

實(shí)例一:內(nèi)存非法訪問

這類問題中最常見的就是內(nèi)存非法訪問。內(nèi)存非法訪問在UNIX平臺即segmentation fault,在Windows平臺這類錯誤稱為Access violation。

內(nèi)存非法訪問是指:進(jìn)程在運(yùn)行時嘗試訪問尚未分配(即,沒有將物理內(nèi)存映射進(jìn)入進(jìn)程虛擬內(nèi)存空間)的內(nèi)存,或者進(jìn)程嘗試向只讀內(nèi)存區(qū)域?qū)懭霐?shù)據(jù)。當(dāng)進(jìn)程執(zhí)行內(nèi)存非法訪問操作時,內(nèi)存管理單元MMU會產(chǎn)生內(nèi)存保護(hù)異常GPF(General Protection Fault),異常號是13。系統(tǒng)會立刻暫停進(jìn)程的非法操作,并且跳轉(zhuǎn)到GPF的異常處理程序,終止進(jìn)程運(yùn)行。

這種編程錯誤在編譯階段編譯器不會報錯,是運(yùn)行時出現(xiàn)的錯誤。清單3是內(nèi)存非法訪問的一個簡單實(shí)例,進(jìn)程在執(zhí)行第5行代碼時執(zhí)行非法內(nèi)存訪問,異常處理函數(shù)終止進(jìn)程運(yùn)行。

清單3.內(nèi)存非法訪問實(shí)例demoSegfault.c

  1. 1 #include<stdio.h>   
  2. 2 int main()   
  3. 3 {   
  4. 4      char* str = "hello";   
  5. 5      str[0] = 'H';   
  6. 6      return 0;   
  7. 7 }   
  8. 編譯并運(yùn)行:  
  9.  [root@machine ~]# gcc demoSegfault.c -o demoSegfault   
  10.  [root@machine ~]# ./demoSegfault   
  11.  Segmentation fault (core dumped)   
  12.  [root@machine ~]# gdb demoSegfault core.24065   
  13.  ( 已省略不相干文本 )   
  14.  Core was generated by `./demoSegfault'.   
  15.  Program terminated with signal 11, Segmentation fault.  

分析:實(shí)例中,字符串str是存儲在內(nèi)存只讀區(qū)的字符串常量,而第5行代碼嘗試更改只讀區(qū)的字符,所以這是內(nèi)存非法操作。

進(jìn)程從開始執(zhí)行到異常退出經(jīng)歷如下幾步:

進(jìn)程執(zhí)行第5行代碼,嘗試修改只讀內(nèi)存區(qū)的字符;

內(nèi)存管理單元MMU檢查到這是非法內(nèi)存操作,產(chǎn)生保護(hù)內(nèi)存異常GPF,異常號13;

處理器立刻暫停進(jìn)程運(yùn)行,跳轉(zhuǎn)到 GPF 的異常處理函數(shù),異常處理函數(shù)終止進(jìn)程運(yùn)行;

進(jìn)程segmentation fault,并且產(chǎn)生core dump文件。GDB調(diào)試結(jié)果顯示,進(jìn)程異常退出的原因是segmentation fault。

實(shí)例二:除0操作

實(shí)例二是除0操作,軟件開發(fā)中也會引入這樣的錯誤。當(dāng)進(jìn)程執(zhí)行除 0 操作時,處理器上的浮點(diǎn)單元FPU(Floating-point unit)會產(chǎn)生DEE除0異常(Divide Error Exception),異常號是0。

清單4.除0操作divide0.c

  1.      1 #include <stdio.h>   
  2.      2   
  3.      3 int main()   
  4.      4 {   
  5.      5     int a = 1, b = 0, c;   
  6.      6     printf( "Start running\n" );   
  7.      7     c = a/b ;   
  8.      8     printf( "About to quit\n" );   
  9.      9 }   
  10. 譯并運(yùn)行:  
  11. [root@machine ~]# gcc -o divide0 divide0.c   
  12. [root@machine ~]# ./divide0 &   
  13. [1] 1229   
  14. [root@machine ~]# Start running   
  15. [1]+  Floating point exception(core dumped) ./divide0   
  16. [root@xbng103 ~]# gdb divide0 /corefiles/core.1229   
  17. ( 已省略不相干文本 )   
  18. Core was generated by `./divide0'.   
  19. Program terminated with signal 8, Arithmetic exception.  

分析:實(shí)例中,代碼第7行會執(zhí)行除0操作,導(dǎo)致異常出現(xiàn),異常處理程序終止進(jìn)程運(yùn)行,并且輸出錯誤提示:Floating point exception。#p#

異常處理函數(shù)內(nèi)幕

異常處理函數(shù)在實(shí)現(xiàn)上,是通過向掛起進(jìn)程發(fā)送信號,進(jìn)而通過信號的默認(rèn)信號處理程序終止進(jìn)程運(yùn)行,所以異常處理函數(shù)是“間接”終止進(jìn)程運(yùn)行。詳細(xì)過程如下:

  1. 進(jìn)程執(zhí)行非法指令或執(zhí)行錯誤操作;
  2. 非法操作導(dǎo)致處理器異常產(chǎn)生;
  3. 系統(tǒng)掛起進(jìn)程,讀取異常號并且跳轉(zhuǎn)到相應(yīng)的異常處理函數(shù);

 (1)異常處理函數(shù)首先查看異常是否可以恢復(fù)。如果無法恢復(fù)異常,異常處理函數(shù)向進(jìn)程發(fā)送信號。發(fā)送的信號根據(jù)異常類型而定,比如內(nèi)存保護(hù)異常GPF相對應(yīng)的信號是SIGSEGV,而除0異常DEE相對應(yīng)的信號是SIGFPE;

 (2)異常處理函數(shù)調(diào)用內(nèi)核函數(shù) issig() 和 psig() 來接收和處理信號。內(nèi)核函數(shù) psig() 執(zhí)行默認(rèn)信號處理程序,終止進(jìn)程運(yùn)行;

  4. 進(jìn)程異常退出。

在此基礎(chǔ)上,我們可以把圖2進(jìn)一步細(xì)化如下: 

圖3. 異常處理函數(shù)終止進(jìn)程運(yùn)行(細(xì)化)

圖 3. 異常處理函數(shù)終止進(jìn)程運(yùn)行(細(xì)化)

異常處理函數(shù)執(zhí)行時會檢查異常號,然后根據(jù)異常類型發(fā)送相應(yīng)的信號。

再來看一下實(shí)例一(代碼清單 3)的運(yùn)行結(jié)果:

  1. [root@machine ~]# ./demoSegfault   
  2.  Segmentation fault (core dumped)   
  3.  [root@machine ~]# gdb demoSegfault core.24065   
  4.  ( 已省略不相干文本 )   
  5.  Core was generated by `./demoSegfault'.   
  6.  Program terminated with signal 11, Segmentation fault.  

運(yùn)行結(jié)果顯示進(jìn)程接收到信號 11 后異常退出,在signal.h的定義里,11就是SIGSEGV。MMU產(chǎn)生內(nèi)存保護(hù)異常GPF異常號 13時,異常處理程序發(fā)送相應(yīng)信號SIGSEGV,SIGSEGV的默認(rèn)信號處理程序終止進(jìn)程運(yùn)行。

再來看實(shí)例二(代碼清單 4)的運(yùn)行結(jié)果

  1. [root@machine ~]# ./divide0 &   
  2.  [1] 1229   
  3.  [root@machine ~]# Start running   
  4.  [1]+  Floating point exception(core dumped) ./divide0   
  5.  [root@xbng103 ~]# gdb divide0 /corefiles/core.1229   
  6.  ( 已省略不相干文本 )   
  7.  Core was generated by `./divide0'.   
  8.  Program terminated with signal 8, Arithmetic exception.  

分析結(jié)果顯示進(jìn)程接收到信號8后異常退出,在signal.h 的定義里,8就是信號SIGFPE。除0操作產(chǎn)生異常(異常號 0),異常處理程序發(fā)送相應(yīng)信號SIGFPE給掛起進(jìn)程,SIGFPE 的默認(rèn)信號處理程序終止進(jìn)程運(yùn)行。

“信號”是進(jìn)程異常退出的直接原因

信號與進(jìn)程異常退出有著緊密的關(guān)系:第一類情況是因?yàn)橥獠凯h(huán)境向進(jìn)程發(fā)送信號,這種情況下發(fā)送的信號是異步信號,信號的到來與進(jìn)程的運(yùn)行是異步的;第二類情況是進(jìn)程非法操作觸發(fā)處理器異常,然后異常處理函數(shù)在內(nèi)核態(tài)向進(jìn)程發(fā)送信號,這種情況下發(fā)送的信號是同步信號,信號的到來與進(jìn)程的運(yùn)行是同步的。這兩種情況都有信號產(chǎn)生,并且最終都是信號處理程序終止進(jìn)程運(yùn)行。它們的區(qū)別是信號產(chǎn)生的信號源不同,前者是外部信號源產(chǎn)生異步信號,后者是進(jìn)程自身作為信號源產(chǎn)生同步信號。

所以,信號是進(jìn)程異常退出的直接原因。當(dāng)進(jìn)程異常退出時,進(jìn)程必然接收到了信號。#p#

避免和調(diào)試進(jìn)程異常退出

建議

軟件開發(fā)過程中,我們應(yīng)當(dāng)避免進(jìn)程異常退出,針對導(dǎo)致進(jìn)程異常退出的這兩類問題,對軟件開發(fā)者的幾點(diǎn)建議:

  1. 通常情況無需屏蔽外部信號。信號作為進(jìn)程間的一種通信方式,異步信號到來意味著外部要求進(jìn)程的退出;
  2. 綁定自定義信號處理程序做清理工作,當(dāng)外部信號到來時,確保進(jìn)程異常退出前,自定義信號處理程序被調(diào)用做清理工作,比如刪除創(chuàng)建的臨時文件。
  3. 針對第二類情況,編程過程中確保進(jìn)程不要做非法操作,尤其是在訪問內(nèi)存時,確保內(nèi)存已經(jīng)分配給進(jìn)程(映射入進(jìn)程虛擬地址空間),不要向只讀區(qū)寫入數(shù)據(jù)。

問題調(diào)試和定位

進(jìn)程異常退出時,操作系統(tǒng)會產(chǎn)生 core dump 文件,cored ump 文件是進(jìn)程異常退出前內(nèi)存狀態(tài)的快照,運(yùn)行 GDB 分析 core dump 文件可以幫助調(diào)試和定位問題。

1) 首先,分析 core dump 查看導(dǎo)致進(jìn)程異常退出的具體信號和退出原因。

使用 GDB 調(diào)試實(shí)例一(代碼清單 3)的分析結(jié)果如下:

  1. [root@machine ~]# gdb demoSegfault core.24065   
  2.  ( 已省略不相干文本 )   
  3.  Core was generated by `./demoSegfault'.   
  4.  Program terminated with signal 11, Segmentation fault.  

分析結(jié)果顯示,終止進(jìn)程運(yùn)行的信號是 11,SIGSEGV,原因是內(nèi)存非法訪問。

2) 然后,定位錯誤代碼。

在 GDB 分析 core dump 時,輸入“bt”指令打印進(jìn)程退出時的代碼調(diào)用鏈,即 backtrace,就可以定位到錯誤代碼。

用 gcc 編譯程序時加入?yún)?shù) -g 可以生成符號文件,幫助調(diào)試。

重新編譯、執(zhí)行實(shí)例一,并且分析 core dump 文件,定位錯誤代碼:

  1. [root@machine ~]# gcc -o demoSegfault demoSegfault.c -g   
  2.  [root@machine ~]# ./demoSegfault  &   
  3.  [1] 28066   
  4.  [1]+  Segmentation fault      (core dumped) ./demoSegfault   
  5.  [root@machine ~]# gdb demoSegfault /corefiles/core.28066   
  6.  ( 已省略不相干文本 )   
  7.  Core was generated by `./demoSegfault'.   
  8.  Program terminated with signal 11, Segmentation fault.   
  9.  #0  0x0804835a in main () at demoSegfault.c:5   
  10.  5               str[0] = 'H';   
  11.  (gdb) bt   
  12.  #0  0x0804835a in main () at demoSegfault.c:5   
  13.  (gdb)  

在加了參數(shù) -g 編譯后,我們可以用 gdb 解析出更多的信息幫助我們調(diào)試。在輸入“bt”后,GDB 輸出提示錯誤出現(xiàn)在第 5 行。

3) 最后,在定位到錯誤代碼行后,就可以很快知道根本原因,并且修改錯誤代碼。

責(zé)任編輯:黃丹 來源: developerWorks
相關(guān)推薦

2010-04-15 11:21:56

2010-04-15 10:41:13

2010-04-07 14:54:20

Unix操作系統(tǒng)

2010-03-24 13:56:41

云計(jì)算

2010-04-15 18:39:56

Unix操作系統(tǒng)

2010-04-29 16:46:59

Unix進(jìn)程

2010-04-14 13:59:45

Unix操作系統(tǒng)

2010-04-16 18:19:32

Unix操作系統(tǒng)

2010-04-14 15:58:25

Unix操作系統(tǒng)

2010-05-11 19:16:03

Unix系統(tǒng)

2010-04-09 17:25:13

Unix操作系統(tǒng)

2010-05-06 16:15:04

Unix系統(tǒng)進(jìn)程

2010-04-14 13:20:29

Unix操作系統(tǒng)

2010-04-19 09:08:20

Unix操作系統(tǒng)

2010-04-19 16:47:40

Unix操作系統(tǒng)

2009-06-17 09:06:59

Unix系統(tǒng)資源進(jìn)程

2010-04-08 10:42:28

Unix操作系統(tǒng)

2023-10-12 22:35:08

2010-04-14 16:45:15

Unix操作系統(tǒng)

2010-04-30 17:27:59

Unix操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號