• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

               C++ 技術(shù)中心

               :: 首頁(yè) :: 聯(lián)系 ::  :: 管理
              160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

            公告

            鄭重聲明:本BLOG所發(fā)表的原創(chuàng)文章,作者保留一切權(quán)利。必須經(jīng)過(guò)作者本人同意后方可轉(zhuǎn)載,并注名作者(天空)和出處(CppBlog.com)。作者Email:coder@luckcoder.com

            留言簿(27)

            搜索

            •  

            最新隨筆

            最新評(píng)論

            評(píng)論排行榜

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


            進(jìn)程異常退出

            進(jìn)程退出意味著進(jìn)程生命期的結(jié)束,系統(tǒng)資源被回收,進(jìn)程從操作系統(tǒng)環(huán)境中銷毀。進(jìn)程異常退出是進(jìn)程在運(yùn)行過(guò)程中被意外終止,從而導(dǎo)致進(jìn)程本來(lái)應(yīng)該繼續(xù)執(zhí)行的任務(wù)無(wú)法完成。

            進(jìn)程異常退出可能給軟件用戶造成如下負(fù)面影響:

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

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

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

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

            • 第一類:向進(jìn)程發(fā)送信號(hào)導(dǎo)致進(jìn)程異常退出;
            • 第二類:代碼錯(cuò)誤導(dǎo)致進(jìn)程運(yùn)行時(shí)異常退出。

            回頁(yè)首

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

            信號(hào)

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

            信號(hào)處理程序:

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

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

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

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


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

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

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

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

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

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


            清單 1. 調(diào)用 kill() 函數(shù)發(fā)送信號(hào)
            				  /* sendSignal.c, send the signal ‘ SIGSEGV ’ to specific process*/        1 #include <sys/types.h>        2 #include <signal.h>        3        4 int main(int argc, char* argv[])        5 {        6     char* pid = argv[1];        7     int PID = atoi(pid);        8        9     kill(PID, SIGSEGV);       10     return 0;       11 }  

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

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

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

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

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

            kill SIG*** PID

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

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

            操作演示如下:

             [root@machine ~]# top &   [1] 22810   [root@machine ~]# kill -SIGSEGV 22810   [1]+  Stopped                 top   [root@machine ~]# fg %1   top   Segmentation fault (core dumped)  

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

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

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

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

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

            對(duì)這類情況的思考

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

            signal 函數(shù)的原型:

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

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

            (void)signal(SIGINT, SIG_IGN);

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

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


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

            回頁(yè)首

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

            相比于第一類情況,第二類情況在軟件開(kāi)發(fā)過(guò)程中是常客,是編程錯(cuò)誤,進(jìn)程運(yùn)行過(guò)程中非法操作引起的。

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

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

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

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

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

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

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

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


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

            實(shí)例以及分析

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

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

            內(nèi)存非法訪問(wèn)是指:進(jìn)程在運(yùn)行時(shí)嘗試訪問(wèn)尚未分配(即,沒(méi)有將物理內(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)存非法訪問(wèn)操作時(shí),內(nèi)存管理單元 MMU 會(huì)產(chǎn)生內(nèi)存保護(hù)異常 GPF(General Protection Fault),異常號(hào)是 13。系統(tǒng)會(huì)立刻暫停進(jìn)程的非法操作,并且跳轉(zhuǎn)到 GPF 的異常處理程序,終止進(jìn)程運(yùn)行。

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


            清單 3. 內(nèi)存非法訪問(wèn)實(shí)例 demoSegfault.c
            				       1 #include<stdio.h>        2 int main()        3 {        4      char* str = "hello";        5      str[0] = 'H';        6      return 0;        7 }  編譯并運(yùn)行:  [root@machine ~]# gcc demoSegfault.c -o demoSegfault   [root@machine ~]# ./demoSegfault   Segmentation fault (core dumped)   [root@machine ~]# gdb demoSegfault core.24065   ( 已省略不相干文本 )   Core was generated by `./demoSegfault'.   Program terminated with signal 11, Segmentation fault.  

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

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

            1. 進(jìn)程執(zhí)行第 5 行代碼,嘗試修改只讀內(nèi)存區(qū)的字符;
            2. 內(nèi)存管理單元 MMU 檢查到這是非法內(nèi)存操作,產(chǎn)生保護(hù)內(nèi)存異常 GPF,異常號(hào) 13;
            3. 處理器立刻暫停進(jìn)程運(yùn)行,跳轉(zhuǎn)到 GPF 的異常處理函數(shù),異常處理函數(shù)終止進(jìn)程運(yùn)行;
            4. 進(jìn)程 segmentation fault,并且產(chǎn)生 core dump 文件。GDB 調(diào)試結(jié)果顯示,進(jìn)程異常退出的原因是 segmentation fault。

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

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


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

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

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

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

            1. 進(jìn)程執(zhí)行非法指令或執(zhí)行錯(cuò)誤操作;
            2. 非法操作導(dǎo)致處理器異常產(chǎn)生;
            3. 系統(tǒng)掛起進(jìn)程,讀取異常號(hào)并且跳轉(zhuǎn)到相應(yīng)的異常處理函數(shù);
              1. 異常處理函數(shù)首先查看異常是否可以恢復(fù)。如果無(wú)法恢復(fù)異常,異常處理函數(shù)向進(jìn)程發(fā)送信號(hào)。發(fā)送的信號(hào)根據(jù)異常類型而定,比如內(nèi)存保護(hù)異常 GPF 相對(duì)應(yīng)的信號(hào)是 SIGSEGV,而除 0 異常 DEE 相對(duì)應(yīng)的信號(hào)是 SIGFPE;
              2. 異常處理函數(shù)調(diào)用內(nèi)核函數(shù) issig() 和 psig() 來(lái)接收和處理信號(hào)。內(nèi)核函數(shù) psig() 執(zhí)行默認(rèn)信號(hào)處理程序,終止進(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í)行時(shí)會(huì)檢查異常號(hào),然后根據(jù)異常類型發(fā)送相應(yīng)的信號(hào)。

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

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

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

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

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

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

            回頁(yè)首

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

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

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

            回頁(yè)首

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

            建議

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            posted on 2013-09-30 16:50 C++技術(shù)中心 閱讀(1696) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Linux 編程
            国内精品人妻无码久久久影院| 伊色综合久久之综合久久| 99久久99久久精品国产片果冻| 国产成人无码精品久久久免费| 日本精品久久久久影院日本| 国内精品伊人久久久影院| 欧美黑人激情性久久| 久久青青草原国产精品免费| 无码乱码观看精品久久| 久久久久无码精品国产| 很黄很污的网站久久mimi色| 久久免费视频1| 久久综合丝袜日本网| yy6080久久| 99久久国产综合精品网成人影院| 国产精品久久久久免费a∨| 91精品国产综合久久精品| 亚洲国产精品成人AV无码久久综合影院 | 久久亚洲熟女cc98cm| 久久er热视频在这里精品| 亚洲第一永久AV网站久久精品男人的天堂AV | 国产三级久久久精品麻豆三级 | 久久久高清免费视频| 91精品国产91久久综合| 久久一本综合| 欧美激情精品久久久久| 亚洲AV无码久久精品狠狠爱浪潮 | 亚洲伊人久久成综合人影院 | 囯产精品久久久久久久久蜜桃 | 久久久久亚洲av无码专区导航 | 久久青青国产| 久久狠狠色狠狠色综合| 一本久久a久久精品亚洲| 久久精品亚洲乱码伦伦中文| 狠狠干狠狠久久| 久久精品人成免费| 久久99久国产麻精品66| 亚洲а∨天堂久久精品9966| 精品国产一区二区三区久久蜜臀| 国产精品久久久久…| 久久亚洲精精品中文字幕|