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);
}
In your case, a plain
SA_RESTART
and thewaitpid()
in the handler probably suffices. When exit code is uninteresting, you can passSA_NOCLDWAIT
additionally.When client exit must be handled in a more complex way, you can catch
EINTR
in the main program and callwaitpid()
there. To make it race free, you should usepselect()
to block the signal. Alternatively, you can create a signalfd and use it in select/poll with yoursockfd
.Child should use
_exit()
to prevent execution of atexit() handlers (e.g. which write termination entries into a file).