Правильный способ обработки SIGCHLD, EINT и accept() в Linux

У меня есть программа, которая создает TCP-сервер. Когда accept() подключается к клиенту, я выполняю fork() и обрабатываю соединение. Когда этот клиент покидает его, он вызывает waitpid() из-за SIGCHLD, но это вызывает EINTR в 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);
}

1 ответ

В твоем случае равнина SA_RESTART и waitpid() в обработчике наверное хватит. Когда код выхода неинтересен, вы можете передать SA_NOCLDWAIT дополнительно.

Когда выход клиента должен быть обработан более сложным способом, вы можете поймать EINTR в основной программе и звоните waitpid() там. Чтобы сделать его свободным, вы должны использовать pselect() заблокировать сигнал. Кроме того, вы можете создать signalfd и использовать его в select/poll с вашим sockfd,

Ребенок должен использовать _exit() предотвратить выполнение обработчиков atexit() (например, которые записывают записи завершения в файл).

Другие вопросы по тегам