我的 POSIX 信号处理程序中的竞争条件

发布于 2024-11-17 01:13:07 字数 2407 浏览 4 评论 0原文

以下程序分叉出一个子程序,该子程序重复运行“/bin/sleep 10”。父级安装 SIGINT 信号处理程序,将 SIGINT 传递给子级。然而,有时向子进程发送 SIGINT 会失败。这是为什么?我想念什么?

#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_SUCCESS);
        }

    }

    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 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_SUCCESS);
        }

    }

    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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

跨年 2024-11-24 01:13:07

当您键入 Ctrl + C 时,tty 驱动程序会对整个进程组执行 SIGINT;这包括子进程,该子进程将响应它而退出,因为它没有安装处理程序。因此,您正在重复已经完成的操作,并且当父级尝试 kill() 时子级是否仍然存在,这是一个冒险的事情。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文