Linux下多執行緒程式設計與訊號處理易疏忽的一個例子(轉)

ba發表於2007-08-12
Linux下多執行緒程式設計與訊號處理易疏忽的一個例子(轉)[@more@]這幾天把一個網路流量採集器程式基本改好了,原來在main函式中把幾個子執行緒啟動後就睡10分鐘後開始清理子執行緒後退出。現在想改成子執行緒啟動後主執行緒進入無限睡眠,直到收到SIGTERM或SIGINT。主程式如下:
其他標頭檔案
#include //訊號處理所需要的標頭檔案
int main(int argc, char * argv[]){
//其他所需要的變數宣告
sigset_t sig_set,sig_pending;


// 設定訊號阻塞
sigemptyset(&sig_set);
sigaddset(&sig_set,SIGTERM);
sigaddset(&sig_set,SIGINT);
sigprocmask(SIG_BLOCK,&sig_set,NULL);


啟動幾個子執行緒
...........

// 設定訊號阻塞
sigemptyset(&sig_set);
sigaddset(&sig_set,SIGTERM);
sigaddset(&sig_set,SIGINT);
sigprocmask(SIG_BLOCK,&sig_set,NULL);

//主執行緒進入睡眠,等待訊號到達後跳出睡眠
while(1){
sigpending(&sig_pending);
if(sigismember(&sig_pending, SIGTERM)||
sigismember(&sig_pending,SIGINT)){
break;
}
sleep(2);
}

//子執行緒退出情理
................
return 0;

}

程式執行後發現 當按下Ctrl+C後程式沒有出現子執行緒退出時的資訊而是立刻退出,非常奇怪。
仔細分析了一下,發現問題在於忽略了Linux下的多執行緒模型的特點。

Linux下的執行緒實質上是輕量級程式(light weighted process),執行緒生成時會生成對應的程式控制結構,只是該結構與父執行緒的程式控制結構共享了同一個程式記憶體空間。 同時新執行緒的程式控制結構將從父執行緒(程式)處複製得到同樣的程式資訊,如開啟檔案列表和訊號阻塞掩碼等。由於我們是在子執行緒生成之後修改了訊號阻塞掩碼,此刻子執行緒使用的是主執行緒原有的程式資訊,因此子執行緒仍然會對SIGINT和SIGTERM訊號進行反應,因此當我們用Ctrl+C發出了SIGINT訊號的時候,主程式不處理該訊號,而子程式(執行緒)會進行預設處理,即退出。子程式退出的同時會向父程式(執行緒)傳送SIGCHLD訊號,表示子程式退出,由於該訊號沒有被阻塞,因此會導致主程式(執行緒)也立刻退出,出現了前述的執行情況。因而該問題的一個解決方法是在子執行緒生成前進行訊號設定, 或在子執行緒內部進行訊號設定。 由於子執行緒是往往是一個事務處理函式,因此我建議在簡單的情況下采用前者,如果需要處理的訊號比較複雜,那就必須使用後一種方法來處理。這樣,以上的程式邏輯改為如下就可以了:

#include //訊號處理所需要的標頭檔案
int main(int argc, char * argv[]){
//其他所需要的變數宣告
sigset_t sig_set,sig_pending;
啟動幾個子執行緒
...........


//主執行緒進入睡眠,等待訊號到達後跳出睡眠
while(1){
sigpending(&sig_pending);
if(sigismember(&sig_pending, SIGTERM)||
sigismember(&sig_pending,SIGINT)){
break;
}
sleep(2);
}

//子執行緒退出情理
................
return 0;

}

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-950193/,如需轉載,請註明出處,否則將追究法律責任。

相關文章