2015-09-11 56 views
0

我有一個創建TCP服務器的程序。當accept()連接到客戶端時,我fork()它並處理連接。當客戶端由於SIGCHLD而離開它時調用waitpid(),但是這導致accept()中的EINTR。我的問題是應該如何處理?我讀過很多不同的方法。在Linux中處理SIGCHLD,EINT和accept()的正確方法

大多數人說忽略它的EINT並再次嘗試accept()。我甚至見過一個宏來做到這一點:TEMP_FAILURE_RETRY()。有人說設置sigaction標誌SA_RESTART和SA_NOCLDSTOP。我試過了,它引入了其他錯誤(errno = ECHILD)。另外,孩子應該如何退出?我見過_exit(0)和exit(0)。

int main(int argc, char *argv[]) 
{ 
    int sockfd, newsockfd, clilen; 
    struct sockaddr_in cli_addr; 
    int pid; 

    f_SigHandler(); 
    sockfd = f_SetupTCPSocket(); 
    clilen = sizeof(cli_addr); 

    while (1) 
    { 
     newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 
     if (newsockfd < 0) 
     { 
     if(errno == EINTR) continue; 
     else     exit(1) ; 
     } 

     pid = fork(); 
     if (pid == 0) 
     { 
     close(sockfd); 
     doprocessing(); 
     close(newsockfd); 
     _exit(0); 
     } 
     else 
     { 
     close(newsockfd); 
     } 
    } 
} 

的SIGCHLD的處理是:

void f_ChildHandler(int signal_number) 
{ 
    while (waitpid(-1, NULL, WNOHANG) > 0) 
    { 
    } 
} 

void f_SigHandler(void) 
{ 
    struct sigaction sa; 

    memset(&sa, 0, sizeof(sa)); 
    sa.sa_handler = f_ChildHandler; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = 0; 
    sigaction(SIGCHLD, &sa, NULL); 
} 
+0

如果你不需要退出狀態,你可以忽略'SIGCHLD'。請參閱http://stackoverflow.com/questions/17015830/how-can-i-prevent-zombie-child-processes – Barmar

回答

0

在你的情況,一個普通的SA_RESTART並在處理程序可能就足夠了waitpid()。退出代碼無趣時,您可以另外通過SA_NOCLDWAIT

當客戶端出口必須以更復雜的方式處理時,您可以在主程序中捕獲EINTR,並在那裏調用waitpid()。爲了使它免費競賽,您應該使用pselect()來阻止信號。或者,您可以創建一個signalfd並在您的sockfd的select/poll中使用它。

子應該使用_exit()來防止執行atexit()處理程序(例如,將終止條目寫入文件中)。

+0

當存在一個單一的孩子並且該孩子_exit()的s,f_ChildHandler()被調用時。第一次通過while循環的waitpid()返回剛退出的孩子的pid。第二遍,waitpid()返回-1並將errno設置爲10(ECHILD)。我期望這是因爲沒有更多的孩子,這就是waitpid()在沒有更多孩子時應該做的事情。所以,如果我想運行「無錯誤」,我應該在f_ChildHandler()中調用waitpid()一次並設置SA_RESTART? –

相關問題