Получение родительского printf только при запуске образца кода GeeksforGeeks - PullRequest
0 голосов
/ 06 июля 2019

Я пытаюсь сосредоточиться на сигналах, поэтому я начал с кода на странице сигналов 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 голосов
/ 06 июля 2019

Этот код полон ошибок.Вы должны начать с включения предупреждений на вашем компиляторе.На GCC и clang это делается путем передачи флагов -pedantic и уровня предупреждения.Я предлагаю использовать -Wall -Wextra -Werror - это дает довольно строгие предупреждения, не мешая.

Первые две ошибки, как отмечено в комментарии, заключаются в том, что объявления прототипов являются неправильными, и что функция mainподпись неверна.

Вторая ошибка заключается в том, что вы не можете безопасно вызывать printf внутри обработчика сигнала (вы встретите это относительно часто в коде, но это неправильно ).Фактически, набор действий, которые вы можете безопасно выполнить внутри обработчика сигнала, строго ограничен - внимательно прочитайте документацию .

В-третьих, попытка кода сброситьобработчики не работают (и в любом случае не нужны). Если вы хотите сбросить обработчик, вам нужно передать 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;
}
...