2011-11-27 74 views
1

我試圖寫一個信號處理程序來捕捉任意數量的連續的SIGINT信號並防止程序退出。該程序是一個簡單的文件服務器。該處理程序設置一個全局標誌,使while循環接受新的連接結束,調用pthread_exit()可確保main在退出前讓當前連接完成。這一切都像發條一樣,當我按ctrl-C一次,但第二次立即退出程序。持續信號處理

我第一次嘗試用信號():

signal(SIGINT, catch_sigint); 

... 

static void catch_sigint(int signo) 
{ 
    ... 
    signal(SIGINT, catch_sigint); 
} 

我也嘗試過使用的sigaction:

struct sigaction sigint_handler; 
sigint_handler.sa_handler = catch_sigint; 
sigemptyset(&sigint_handler.sa_mask); 
sigint_handler.sa_flags = 0; 
sigaction(SIGINT, &sigint_handler, NULL); 

不確定如何「重新安裝」這一個我只是複製這個代碼在類似的處理程序使用signal()方法處理處理程序。

這兩個都沒有如我所料。


附加信息:

該方案是一個簡單的文件服務器。它接收來自客戶端的請求,該請求只是由所請求的文件名組成的字符串。它利用pthreads,以便傳輸可以同時發生。收到SIGINT後,我希望服務器退出while循環並等待所有當前傳輸完成,然後關閉。就像,不管我如何編碼信號處理程序,第二個SIGINT立即終止程序。

int serverStop = 0; 

... 

int main() 
{ 
    /* set up the server -- socket(), bind() etc. */ 

    struct sigaction sigint_hadler; 
    sigint_handler.sa_handler = catch_sigint; 
    sigint_handler.sa_flags = 0; 
    sigemptyset(&sigint_handler.sa_mask); 
    sigaction(SIGINT, &sigint_handler, NULL); 

    /* signal(SIGINT, catch_sigint); */ 

    while(serverStop == 0) 
    { 
     /* accept new connections and pthread_create() for each */ 
    } 
    pthread_exit(NULL); 
} 

... 

static void catch_sigint(int signo) 
{ 
    serverStop = 1; 

    /* signal(SIGINT, catch_sigint) */ 
} 

我不認爲任何其他代碼可能是相關的,但隨時索要闡述

回答

1

注意:這很大程度上是猜測。

我很確定在主線程中調用pthread_exit是一個壞主意。如果主線程退出,則操作系統可能會嘗試將後續信號發送到其他線程。

我建議不要在主線程中使用pthread_exit,而只需pthread_join()所有其他線程,然後正常退出。

但確保其他線程無法獲得信號也很重要。通常這是通過sigprocmask(或者更準確地說pthread_sigmask,在Linux下是相同的)在工作者線程中屏蔽掉信號完成的。這確保信號永遠不會傳遞給它們。

請注意,爲避免競爭條件,您應在創建子線程之前在主線程中使用pthread_sigmask,然後在主線程中再次設置信號掩碼。這確保沒有窗口,但是很小,在此期間,子線程可能會得到不需要的信號。

+1

這是一個非常虛假的答案:在主線程中調用pthread_exit *是非常好的*,對於這個特定的信號處理程序,無論信號處理程序調用哪個線程都沒有區別。也不保證在主線程中傳遞'SIGINT'(當它繼續存在時)。 –

+0

目前我沒有限制線程的最大數量(因爲我知道有多少客戶端請求我要做),所以我沒有跟蹤每個線程的ID,爲了使用'pthread_join ()'我相信。但是,您的解釋使問題更清楚。正如我的問題所述,我正在考慮將main中的'pthread_exit()'作爲主要等待,而不是退出主程序而不終止其他線程。使用'pthread_sigmask()'在每個線程中阻止SIGINT已經解決了這個問題。它可能不是最正確的解決方案,但它現在會做。謝謝! – Matt

0

我不知道理解。信號處理程序通常不應該重新安裝任何信號處理程序(包括其自身),因爲信號處理程序會保持功能,直到安裝另一個程序。另請參閱SA_NODEFER標誌至sigaction以在處理期間能夠捕獲信號。

信號處理程序應該很短。看到我的回答this question。它通常主要設置一個volatile sig_atomic_t變量。

什麼不起作用?不要在信號處理器內部進行復雜或長時間的處理。 請出示你的代碼...

+0

「信號處理程序保持功能,直到另一個被安裝」。 BSD信號以這種方式工作,而SysV則沒有。 –

+0

但問題是標記* Linux *,我很確定Linux的sigaction正如我所說的那樣。 –

+1

是的,'sigaction'(不含'SA_RESETHAND')不需要重置信號。對於'信號'它取決於(請參閱我的答案和指針;-) –

6

在Linux上,你應該不必重新安裝信號處理程序,即使用signal(其中implements BSD semantics默認情況下)或sigaction

當我按ctrl-C一次,但第二次立即退出程序。

這不是因爲你的處理程序被重置,而可能是因爲你的信號處理程序正在做它不應該做的事情。

這是我會怎麼調試這個問題:在GDB下運行程序和

(gdb) catch syscall exit 
(gdb) catch syscall exit_group 
(gdb) run 

現在稍等一下,讓程序開始工作,並按下Control-C。這會給你(gdb)提示。現在繼續該程序,如果它已收到SIGINTsignal SIGINT(這將調用您的處理程序)。再次重複'Control-C/signal SIGINT'序列。如果您在系統調用exitexit_group中停止,請查看來自哪裏(使用GDB where命令)。

更新:

鑑於您發佈的新代碼,它正是你打電話pthread_exit爲「確保主讓當前連接退出前完成」目前尚不清楚。正如所寫的,您的主線程將在第一個Control-C上退出循環,並繼續呼叫exit,這將而不是等待其他線程完成。

要麼你沒有表現出你的實際代碼或「第二控制-C」是一個紅色的鯡魚,你的第一控制-C帶你出去已經(沒有完成其他線程的工作)。

+0

對不起,我試圖忽略儘可能多的其他程序邏輯,因此我將它遺漏了。在while循環之後立即有另一個pthread_exit()調用。 – Matt