开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜