這幾天把一個網(wǎng)絡(luò)流量采集器程序基本改好了,原來在main函數(shù)中把幾個子線程啟動后就睡10分鐘后開始清理子線程后退出。現(xiàn)在想改成子線程啟動后主線程進入無限睡眠,直到收到SIGTERM或SIGINT。主程序如下:
其他頭文件
#include <signal.h> //信號處理所需要的頭文件
int main(int argc, char * argv[]){
//其他所需要的變量聲明
sigset_t sig_set,sig_pending;
// 設(shè)置信號阻塞
sigemptyset(&sig_set);
sigaddset(&sig_set,SIGTERM);
sigaddset(&sig_set,SIGINT);
sigprocmask(SIG_BLOCK,&sig_set,NULL);
啟動幾個子線程
...........
// 設(shè)置信號阻塞
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;
}
程序運行后發(fā)現(xiàn) 當(dāng)按下Ctrl+C后程序沒有出現(xiàn)子線程退出時的信息而是立刻退出,非常奇怪。
仔細分析了一下,發(fā)現(xiàn)問題在于忽略了Linux下的多線程模型的特點。
Linux下的線程實質(zhì)上是輕量級進程(light weighted process),線程生成時會生成對應(yīng)的進程控制結(jié)構(gòu),只是該結(jié)構(gòu)與父線程的進程控制結(jié)構(gòu)共享了同一個進程內(nèi)存空間。 同時新線程的進程控制結(jié)構(gòu)將從父線程(進程)處復(fù)制得到同樣的進程信息,如打開文件列表和信號阻塞掩碼等。由于我們是在子線程生成之后修改了信號阻塞掩碼,此刻子線程使用的是主線程原有的進程信息,因此子線程仍然會對SIGINT和SIGTERM信號進行反應(yīng),因此當(dāng)我們用Ctrl+C發(fā)出了SIGINT信號的時候,主進程不處理該信號,而子進程(線程)會進行默認處理,即退出。子進程退出的同時會向父進程(線程)發(fā)送SIGCHLD信號,表示子進程退出,由于該信號沒有被阻塞,因此會導(dǎo)致主進程(線程)也立刻退出,出現(xiàn)了前述的運行情況。因而該問題的一個解決方法是在子線程生成前進行信號設(shè)置, 或在子線程內(nèi)部進行信號設(shè)置。 由于子線程是往往是一個事務(wù)處理函數(shù),因此我建議在簡單的情況下采用前者,如果需要處理的信號比較復(fù)雜,那就必須使用后一種方法來處理。這樣,以上的程序邏輯改為如下就可以了:
#include <signal.h> //信號處理所需要的頭文件
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;
}
文章出處:DIY部落(http://www.diybl.com/course/6_system/linux/Linuxjs/20090317/162211.html)