Race condition in my POSIX signal handler
The following program forks off a child, that runs "/bin/sleep 10" repeatedly. The parent installs a signal handler for SIGINT, that delivers SIGINT to the child. However sometimes sending SIGINT to the child fails. Why is that and what do I miss?
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
pid_t foreground_pid = 0;
void sigint_handler(int sig)
{
printf("sigint_handler: Sending SIGINT to process %d\n",
foreground_pid);
if ((foreground_pid != 0) && kill(foreground_pid, SIGCONT) == -1) {
perror("sending SIGINT to forground process failed");
printf("foreground_pid == %d", foreground_pid);
exit(EXIT_FAILURE);
}
foreground_pid = 0;
}
int main(int argc, const char *argv[])
{
while (1) {
pid_t child_pid;
if ((child_pid = fork()) == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (child_pid) { /* parent */
foreground_pid = child_pid;
printf("waiting for child (%d) to complete ...\n", child_pid);
fflush(stdout);
/* install SIGINT signal handler */
struct sigaction sa;
struct sigaction old_handler;
sa.sa_handler = sigint_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_RESETHAND;
sigaction(SIGINT, &sa, NULL);
int status = 0;
/* wait for child to finish */
if (waitpid(child_pid, &status, 0) == -1) {
perror("waitpid failed");
exit(EXIT_FAILURE);
}
printf(" done.\n");
fflush(stdout);
}
else { /* child */
char * const argv[] = { "/bin/sleep", "10", NULL};
if (execve(argv[0], argv, NULL) == -1) {
perror("execve failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCE开发者_如何转开发SS);
}
}
return EXIT_SUCCESS;
}
% make && ./foo
gcc -Wall -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L foo.c -o foo
waiting for child (4582) to complete ...
^Csigint_handler: Sending SIGINT to process 4582
done.
waiting for child (4583) to complete ...
^Csigint_handler: Sending SIGINT to process 4583
sending SIGINT to forground process failed: No such process
foreground_pid == 4583
The tty driver performs a SIGINT
on the entire process group when you type Ctrl + C; this includes the child process, which will exit in response to it because it doesn't have a handler installed. So you're duplicating what is already being done, and whether the child is still around when the parent tries to kill()
it is something of a crapshoot.
精彩评论