运行 GeeksforGeeks 示例信号代码时仅接收 parent printf

Only receiving parent printf when runnnig GeeksforGeeks sample signal code

我试图将注意力集中在信号上,所以我从 运行在 GeeksforGeeks 信号页面上设置代码开始。在他们的页面上,它显示了 parent 和 child 的输出,但是当我在 CLion 中 运行 时,有 9/10 次我只收到 parent 和child 不打印任何内容。 child 每隔一段时间就会打印一次,但我不知道为什么或如何让它始终如一地打印。

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

}

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

前两个错误,如推荐中所述,是原型声明错误,以及 main 函数签名错误。

第二个错误是你不能在信号处理程序中安全地调用printf(你会在代码中遇到这种情况,但这是错误的)。事实上,您可以在信号处理程序中安全执行的一组操作受到 严重 限制 — read the documentation carefully.

第三,代码尝试重置处理程序的尝试是拙劣的(无论如何都是不必要的)。 如果你想重置一个处理程序,你需要传递SIG_DFL作为第二个参数,而不是自定义回调。文档中也对此进行了解释。

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

将这些部分放在一起得到以下内容,您可能会注意到它比您尝试的 non-working 代码复杂得多。

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