0

TCP サーバーを作成するプログラムがあります。accept() がクライアントに接続すると、それを fork() して接続を処理します。そのクライアントが離れると、SIGCHLD のために waitpid() が呼び出されますが、accept() で EINTR が発生します。私の質問は、これをどのように処理する必要があるかです。私は非常に多くの異なる方法を読みました。

ほとんどの人は、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);
}
4

1 に答える 1

0

あなたの場合、プレーンSA_RESTARTwaitpid()ハンドラー内のおそらく十分です。終了コードが面白くない場合は、SA_NOCLDWAIT追加で渡すことができます。

クライアントの終了をより複雑な方法で処理する必要がある場合EINTRは、メイン プログラムでキャッチして、そこで呼び出すwaitpid()ことができます。レースフリーにするにはpselect()、信号をブロックするために使用する必要があります。または、signalfd を作成し、それを select/poll で使用することもできますsockfd

子は_exit()atexit() ハンドラの実行を防ぐために使用する必要があります (たとえば、終了エントリをファイルに書き込む)。

于 2015-09-11T19:21:59.477 に答える