【问题标题】:Race condition in my POSIX signal handler我的 POSIX 信号处理程序中的竞争条件
【发布时间】:2011-09-23 03:53:30
【问题描述】:

以下程序派生出一个孩子,它重复运行“/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

【问题讨论】:

    标签: c posix signals race-condition waitpid


    【解决方案1】:

    当你键入 Ctrl + C 时,tty 驱动程序对整个进程组执行SIGINT;这包括子进程,它将响应它而退出,因为它没有安装处理程序。所以你在复制已经在做的事情,当父母尝试kill() 时孩子是否还在身边,这是一个废话。

    【讨论】:

    • 没错。在我的脑海中,一个解决方案是在 fork 之后在您的子进程中调用 setsid() 以将孩子放入新的会话和组中,这应该可以防止 SIGINT 信号到达他的孩子。
    • @SlappyTheFish: setpgrp()setpgid() 在这种情况下是首选,但我怀疑正确的答案只是让 tty 驱动程序处理它。
    • 另外可以在子进程中安装 SIG_IGN 作为信号处理程序(在 fork 和 execve 之间。
    • @geekosaur 我检查了文档,但我不能 100% 确定为什么在这种情况下使用 setgrp() 如果更喜欢 setid()。是因为不一定要创建新的会话?
    • @SlappyTheFish:事实上在这种情况下并没有太大的实际区别;同一会话中的单独进程组可以附加到终端或从终端分离(这就是fg 的工作方式)。更多的是不相关进程与相关但不应附加到 tty 的进程之间的概念差异。
    猜你喜欢
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    • 2014-09-23
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多