2013-10-31 16 views
3

假設我有一個標誌來指示我用信號啓用的退出條件。然後,我可以將以下處理程序附加到SIGUSR1中。將sig_atomic_t標誌與阻止呼叫一起使用

volatile sig_atomic_t finished = 0; 

void catch_signal(int sig) 
{ 
    finished = 1; 
} 

然後我使用該標誌來確定特定循環何時應該結束。在這種特殊情況下,我有一個線程正在運行(但我相信我的問題也適用於沒有線程的情況,所以請不要關注該部分)。

void *thread_routine(void *arg) 
{ 
    while (!finished) { 
    /* What if the signal happens here? */ 
    if ((clientfd = accept(sockfd, &remote_addr, &addr_size)) == -1) { 
     if (errno == EINTR) 
     continue; 
     /* Error handling */ 
    } 

    handle_client(clientfd); 
    } 
} 

這個循環應該繼續運行,直到我提出我的SIGUSR1信號。當它收到信號時,我希望它儘快停止。由於我有一個阻塞接受調用,我沒有環路浪費CPU週期,這很好,並且信號可以隨時中斷阻塞接受並導致環路終止。

問題是,如代碼中的註釋所示,信號可以在while條件之後但在accept調用之前立即傳遞。然後信號處理程序將finished設置爲true,但在執行恢復後,accept將被調用並無限期地阻止。我怎樣才能避免這種情況,並確保我始終能夠用我的信號終止循環?

假設我仍然想用信號來控制這個,我可以考慮兩種可能的解決方案。第一種是在第一次錯過信號時打開一段時間後重新發出信號的警報。第二個是在套接字上放一個超時值,以便接受一段時間後返回,以便可以再次檢查該標誌。但是這些解決方案更像是解決方法(尤其是因爲我在第二個解決方案中更改了accept的阻止行爲),並且如果有一些更清晰和更直接的解決方案,我想用它來代替。

+0

不是處理多個線程中的整個循環接受(並且作爲「accept」本身,它不是線程安全的,想想如果一個線程'accept'被另一個線程'搶佔'接受'),在「主」線程中執行,並在調用'accept'後創建一個新線程並將新套接字傳遞給它。 –

+0

這個線程只有一個實例。對'handle_client' OTOH的調用可以將工作交給其他線程。所以即使這不是「主要」線程,它幾乎可以工作。也許我在這裏應該做的是取消線程。但如果這不是一個線程我不能這樣做,所以我仍然想知道如何用信號做到這一點。 –

回答

2

Self-Pipe Trick可以在這種情況下使用。 您打開管道並使用select來等待pipefd和sockfd。處理程序將一個char寫入管道。選擇後,檢查FD設置可以幫助您確定您是否可以接受或不接受。

+1

更好的歸功於[self-pipe trick](http://cr.yp.to/docs/selfpipe.html) – pilcrow

1

我意識到這個問題已經過去了一年多了,但是pselect()正是爲這種情況設計的。您可以提供pselect()(和select()一般)與偵聽套接字的文件描述符,並且當存在可用的連接時,這些函數將返回。

一般的做法是,您將所有相關信號都封鎖,然後用信號掩碼呼叫pselect()以解除阻塞。 pselect()將原子:

  1. 阻止信號(S)
  2. 呼叫accept()
  3. 塊的信號(S)時再accept()回報

,所以你基本上可以保證只有一次信號實際上將交付和處理是當pselect()正在運行,並且您不必擔心在您檢查finished之後但在致電accept()之前它被捕獲。換句話說,你要確保無論何時發送信號,它總是會中斷pselect()並將errno設置爲EINTR,因此這是您唯一需要檢查的地方。