0

I have a program that creates a TCP server. When the accept() connects to a client, I fork() it and handle the connection. When that client leaves it calls the waitpid() because of the SIGCHLD, but this causes a EINTR in the accept(). My question is how should this be handled? I've read so many different ways.

Most say to ignore it the EINT and try the accept() again. I've even seen a macro to do just that: TEMP_FAILURE_RETRY(). Some say to set the sigaction flags SA_RESTART and SA_NOCLDSTOP. I've tried that and it introduces other errors (errno = ECHILD). Also, how should the child exit? I've seen both _exit(0) and 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);
      }
   }
}

The SIGCHLD handling is:

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);
}
Rich G.
  • 61
  • 1
  • 1
  • 5
  • If you don't need to get the exit status, you can ignore `SIGCHLD`. See http://stackoverflow.com/questions/17015830/how-can-i-prevent-zombie-child-processes – Barmar Sep 11 '15 at 19:06

1 Answers1

0

In your case, a plain SA_RESTART and the waitpid() in the handler probably suffices. When exit code is uninteresting, you can pass SA_NOCLDWAIT additionally.

When client exit must be handled in a more complex way, you can catch EINTR in the main program and call waitpid() there. To make it race free, you should use pselect() to block the signal. Alternatively, you can create a signalfd and use it in select/poll with your sockfd.

Child should use _exit() to prevent execution of atexit() handlers (e.g. which write termination entries into a file).

ensc
  • 6,704
  • 14
  • 22
  • When a single child exists and that child _exit()'s, f_ChildHandler() is called. The first time through the while loop waitpid() returns the pid of the child that just exited. The second time through, waitpid() returns a -1 and sets errno to 10 (ECHILD). I expected this because there are no more children and that's what waitpid() is supposed to do when there are no more children. So, if I want to run "error free", should I just call waitpid() once in f_ChildHandler() and set SA_RESTART? – Rich G. Sep 17 '15 at 20:21