仅在runnnig GeeksforGeeks采样信号代码时接收父printf

时间:2019-07-06 14:57:56

标签: c signals clion

我试图把注意力集中在信号上,所以我从运行GeeksforGeeks信号页面上的代码开始。在他们的页面上,它显示了父级和子级的输出,但是当我在CLion中运行时,我仅收到9/10次父级的输出,而子级则什么也不打印。孩子偶尔会打印一次,但我不知道为什么或如何使其始终打印。

https://www.geeksforgeeks.org/signals-c-set-2/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void sighup();
void sigint();
void sigquit();

void main() {

    int pid;

    if ((pid = fork()) < 0) {

        perror("fork");
        exit(1);

    }

    if (pid == 0) {

        signal(SIGHUP, sighup);
        signal(SIGINT, sigint);
        signal(SIGQUIT, sigquit);
        for(;;)
            ;

    }

    else {

        printf("\nPARENT: sending SIGUP\n\n");
        kill(pid, SIGHUP);

        sleep(3);
        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid, SIGINT);

        sleep(3);
        printf("\nPARENT: sending SIGQUIT\n\n");
        kill(pid, SIGQUIT);
        sleep(3);

    }

void sighup() {

    signal(SIGHUP, sighup);
    printf("CHILD: I have received a SIGHUP\n");

}

void sigint() {

    signal(SIGINT, sigint);
    printf("CHILD: I have received a SIGINT\n");

}

void sigquit() {

    printf("My parent has killed me");
    exit(0);

}

1 个答案:

答案 0 :(得分:0)

该代码充满了错误。您应该首先在编译器上启用警告。在GCC和clang上,这是通过传递标志-pedantic和警告级别来完成的。我建议使用-Wall -Wextra -Werror-这会给出相当严格的警告,而不会造成干扰。

如前所述,前两个错误是原型声明错误,并且main函数签名错误。

第二个错误是您不能安全地在信号处理程序中调用printf(您会在代码中遇到这种相对常见的情况,但这是错误的)。实际上,您可以在信号处理程序中安全执行的一系列操作受到严格限制-read the documentation carefully

第三,代码重设处理程序的尝试被破坏了(至少没有必要)。 如果要重置处理程序,则需要传递SIG_DFL作为第二个参数,而不是自定义回调。文档中也对此进行了解释。

最后,我们不能保证子进程中的代码先于父进程中的代码执行-特别是,不能保证将设置信号处理程序。在我的测试中,尽管父代码中有sleep语句,但子代码最终不会被执行。这看似很奇怪,但这是完全合法的,需要预料到。

将这些部分放在一起可以为我们提供以下内容,您可能会注意到,这些内容比您尝试的无效代码要复杂得多。

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

static volatile sig_atomic_t child_signal;
static volatile sig_atomic_t setup_done = 0;

void child_signal_handler(int);
void parent_signal_handler(int);

int main() {
    const int parent_pid = getpid();
    int pid;

    // Set up BEFORE calling `fork`
    signal(SIGCHLD, parent_signal_handler);

    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        signal(SIGHUP, child_signal_handler);
        signal(SIGINT, child_signal_handler);
        signal(SIGQUIT, child_signal_handler);
        kill(parent_pid, SIGCHLD);

        for (;;) {
            if (child_signal != 0) {
                switch ((int) child_signal) {
                    case SIGHUP:
                        fprintf(stderr, "CHILD: I have received a SIGHUP\n");
                        break;
                    case SIGINT:
                        fprintf(stderr, "CHILD: I have received a SIGINT\n");
                        break;
                    case SIGQUIT:
                        fprintf(stderr, "My parent has killed me");
                        exit(1);
                }
                child_signal = 0;
            }
        }
    } else {
        while (! setup_done) { sleep(1); }

        printf("\nPARENT: sending SIGUP\n");
        kill(pid, SIGHUP);

        sleep(1);
        printf("\nPARENT: sending SIGINT\n");
        kill(pid, SIGINT);

        sleep(1);
        printf("\nPARENT: sending SIGQUIT\n");
        kill(pid, SIGQUIT);
    }
}

void child_signal_handler(int signo) {
    child_signal = signo;
}

void parent_signal_handler(int signo) {
    if (signo == SIGCHLD) setup_done = 1;
}