Как использовать sigwaitinfo () в C - PullRequest
0 голосов
/ 21 декабря 2018

Я пытаюсь заставить процесс ждать сигнала от его сына.Кажется, не работает.Я не очень люблю C, поэтому мой код может быть ужасным.Вот код:

Код родителей:

sigemptyset (&mask);
sigaddset (&mask, SIGUSR1);
sigprocmask (SIG_SETMASK, &mask, NULL); 
sigwaitinfo(&mask, &info);
sigprocmask (SIG_UNBLOCK, &mask, NULL);

Код детей:

kill(getppid(), SIGUSR1);

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Проверьте следующую программу-пример, example.c :

#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

static inline const char *signal_name(const int signum)
{
    switch (signum) {
    case SIGINT:  return "SIGINT";
    case SIGHUP:  return "SIGHUP";
    case SIGTERM: return "SIGTERM";
    case SIGQUIT: return "SIGQUIT";
    case SIGUSR1: return "SIGUSR1";
    case SIGUSR2: return "SIGUSR2";
    default:      return "(unnamed)";
    }    
}

int main(void)
{
    sigset_t  mask;
    siginfo_t info;
    pid_t     child, p;
    int       signum;    

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        fprintf(stderr, "Cannot block SIGUSR1: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    child = fork();
    if (child == -1) {
        fprintf(stderr, "Cannot fork a child process: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    } else
    if (!child) {
        /* This is the child process. */
        printf("Child process %d sleeping for 3 seconds ...\n", (int)getpid());
        fflush(stdout);
        sleep(3);

        printf("Child process %d sending SIGUSR1 to parent process (%d) ...\n", (int)getpid(), (int)getppid());
        fflush(stdout);
        kill(getppid(), SIGUSR1);

        printf("Child process %d exiting.\n", (int)getpid());
        return EXIT_SUCCESS;
    }

    /* This is the parent process. */
    printf("Parent process %d is waiting for signals.\n", (int)getpid());
    fflush(stdout);

    while (1) {

        signum = sigwaitinfo(&mask, &info);
        if (signum == -1) {

            /* If some other signal was delivered to a handler installed
               without SA_RESTART in sigaction flags, it will interrupt
               slow calls like sigwaitinfo() with EINTR error. So, those
               are not really errors. */
            if (errno == EINTR)
                continue;

            printf("Parent process: sigwaitinfo() failed: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

        if (info.si_pid == child)
            printf("Parent process: Received signal %d (%s) from child process %d.\n", signum, signal_name(signum), (int)child);
        else
        if (info.si_pid)
            printf("Parent process: Received signal %d (%s) from process %d.\n", signum, signal_name(signum), (int)info.si_pid);
        else
            printf("Parent process: Received signal %d (%s).\n", signum, signal_name(signum));
        fflush(stdout);

        /* Exit when SIGUSR1 received from child process. */
        if (signum == SIGUSR1 && info.si_pid == child) {
            printf("Parent process: Received SIGUSR1 from child.\n");
            break;
        }

        /* Also exit if Ctrl+C pressed in terminal (SIGINT). */
        if (signum == SIGINT && !info.si_pid) {
            printf("Parent process: Ctrl+C pressed.\n");
            break;
        }
    }

    printf("Reaping child process...\n");
    fflush(stdout);

    do {
        p = waitpid(child, NULL, 0);
        if (p == -1) {
            if (errno == EINTR)
                continue;
            printf("Parent process: waitpid() failed: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    } while (p != child);

    printf("Done.\n");
    return EXIT_SUCCESS;
}

Скомпилируйте и запустите ее, например,

gcc -Wall -O2 example.c -o example
./example

Вы можете увеличить время, в течение которого ребенокПроцесс спит, если вы хотите, чтобы извне ввести сигнал в родительский процесс.Если идентификатор родительского процесса, скажем, 24316, вы можете отправить его, например.сигнал SIGHUP через kill -HUP 24316 с другого терминала.Если вы запустите пример в интерактивном режиме, как показано выше, вы также можете заставить терминал отправлять сигнал SIGINT процессу, нажав Ctrl + C .

Наблюдения:

  • sigprocmask() используется для блокировки интересных сигналов в родительском процессе до fork(), чтобы родительский процесс мог перехватить сигнал.Это также означает, что сигналы заблокированы в дочернем процессе.

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

  • signal_name() существует только для приятной печати имени сигнала.

    Он помечен static inline главным образом для того, чтобы разработчики-люди понимали, что это вспомогательная функция, видимая только в текущем модуле компиляции.Для компилятора static говорит, что функция видима только в текущем модуле компиляции, а inline говорит, что компилятор может свободно включать свою функциональность в тот, кто ее вызывает, вместо вызова именованной функции.

    Возвращаемое значение const char *, поскольку функция возвращает строковый литерал.

  • fork() может вернуть -1 в случае ошибки.

  • fork() возвращается дважды.В родительском процессе возвращаемое значение является положительным;идентификатор дочернего процесса.В дочернем процессе возвращаемое значение равно нулю.

    Новый дочерний процесс по сути является копией снимка родительского процесса.Порядок, в котором они начинают выполняться, в основном случайный: они могут работать одновременно, оба одновременно;или один может бежать первым, а другой чуть позже.В наши дни компьютеры настолько быстры, что такие понятия, как «достаточно скоро», измеряемые в микросекундах, все же могут привести к ошибкам, поэтому нам нужно быть осторожными и понимать общую картину.Следовательно, ранняя установка маски сигнала.

  • Многие функции возвращают -1 или NULL в случае возникновения ошибки, а errno указывает на ошибку.При написании кода вы всегда должны выполнять проверку ошибок.Они позволяют обнаруживать логические и функциональные ошибки при тестировании кода.В чрезвычайно редком случае, когда они «замедляют» что-либо, вы всегда можете удалить их после профилирования и тестирования.На практике они того стоят, каждый раз;если не для чего-то еще, для того, чтобы поймать неверные ожидания программиста.

  • См. man 2 sigaction и man 7 signal, чтобы увидеть, какие сигналы заполняютсякакие поля siginfo_t и как вы можете определить, был ли сигнал отправлен другим процессом (через kill() или sigqueue()), вызван, вызван таймером POSIX и т. д.

  • См. Цикл while с waitpid() о том, как получить дочерний процесс.Мы могли бы использовать второй параметр, указатель на int и WIFEXITED() / WEXITSTATUS() и WIFSIGNALED() / WTERMSIG(), чтобы проверить состояние выхода дочернего процесса.Я не стал беспокоиться, потому что дочерний процесс всегда возвращает EXIT_SUCCESS, который в системах POSIXy равен 0.

  • Научиться проектировать и создавать программы из модульных частей, а не объединять все в одинPill, а затем попытаться разобраться.

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

    Однако разделение на функции не является целью: это всего лишь инструмент, используемый для того, чтобы сделать код максимально простым и легким для понимания и сопровождения.Мы, люди, обладаем ограниченными умственными способностями, но если мы фокусируем правильно, мы можем создавать удивительные вещи.

  • Хорошие комментарии по крайней мере так же важны, как и хороший код.

    Комментарии, которые описывают, что делает код, бесполезны.Мы можем прочитать код, чтобы увидеть, что он делает.Код не сообщает нам: почему программист написал код: какова цель кода, какова логическая модель или алгоритм, который пытается реализовать код.

    Естьтолько пять комментариев в примере программы.Этого недостаточно;но даже после пары десятилетий написания кода профессионально, я борюсь с написанием лучших комментариев.(В основном, трудно описать ментальные структуры, с которыми я думаю, используя линейный текст. Это все равно, что выучить язык чтением и не в состоянии произнести или понять разговорный язык.) Если бы я научился писать хорошие комментариипоскольку я научился писать хороший код, я бы сэкономил МНОГО усилий.

    Я рекомендую вам избежать разочарований и потратить усилия на то, чтобы научиться писать хорошие комментарии с самого начала.


Вот еще один пример, example2.c , который выполняет немного больше пинг-понга между родительским и дочерним процессами:

#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

static inline const char *signal_name(const int signum)
{
    switch (signum) {
    case SIGINT:  return "SIGINT";
    case SIGHUP:  return "SIGHUP";
    case SIGTERM: return "SIGTERM";
    case SIGQUIT: return "SIGQUIT";
    case SIGUSR1: return "SIGUSR1";
    case SIGUSR2: return "SIGUSR2";
    default:      return "(unnamed)";
    }    
}

int child_process(const pid_t parent, sigset_t *mask)
{
    siginfo_t    info;
    int          signum;

    printf("Child: sleep(1).\n");
    fflush(stdout);
    sleep(1);

    printf("Child: Sending SIGUSR1 to parent.\n");
    fflush(stdout);
    kill(parent, SIGUSR1);

    printf("Child: Waiting for a SIGUSR2 from parent.\n");
    fflush(stdout);
    while (1) {
        signum = sigwaitinfo(mask, &info);
        if (signum == SIGUSR2 && info.si_pid == parent) {
            printf("Child: Received SIGUSR2 from parent.\n");
            break;
        }

        if (info.si_pid == parent)
            printf("Child: Received %s from parent.\n", signal_name(signum));
        else
        if (info.si_pid)
            printf("Child: Received %s from process %d.\n", signal_name(signum), (int)info.si_pid);
        else
            printf("Child: Received %s.\n", signal_name(signum));
        fflush(stdout);
    }

    printf("Child: Sending SIGHUP to parent.\n");
    fflush(stdout);
    kill(parent, SIGHUP);

    printf("Child: sleep(1).\n");
    fflush(stdout);
    sleep(1);

    printf("Child: Done.\n");
    return EXIT_SUCCESS;
}

void parent_process(const pid_t child, sigset_t *mask)
{
    siginfo_t  info;
    int        signum;

    printf("Parent: Waiting for a SIGUSR1 from child.\n");
    while (1) {
        signum = sigwaitinfo(mask, &info);
        if (signum == SIGUSR1 && info.si_pid == child) {
            printf("Parent: Received SIGUSR1 from child.\n");
            break;
        }

        if (info.si_pid == child)
            printf("Parent: Received %s from child.\n", signal_name(signum));
        else
        if (info.si_pid)
            printf("Parent: Received %s from process %d.\n", signal_name(signum), (int)info.si_pid);
        else
            printf("Parent: Received %s.\n", signal_name(signum));
        fflush(stdout);
    }

    printf("Parent: sleep(1).\n");
    fflush(stdout);
    sleep(1);

    printf("Parent: Sending SIGUSR2 to child.\n");
    fflush(stdout);
    kill(child, SIGUSR2);

    printf("Parent: Waiting for a SIGHUP from child.\n");
    while (1) {
        signum = sigwaitinfo(mask, &info);
        if (signum == SIGHUP && info.si_pid == child) {
            printf("Parent: Received SIGHUP from child.\n");
            break;
        }

        if (info.si_pid == child)
            printf("Parent: Received %s from child.\n", signal_name(signum));
        else
        if (info.si_pid)
            printf("Parent: Received %s from process %d.\n", signal_name(signum), (int)info.si_pid);
        else
            printf("Parent: Received %s.\n", signal_name(signum));
        fflush(stdout);
    }

    return;
}

int main(void)
{
    sigset_t  mask;
    pid_t     child, p;
    int       status;

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        fprintf(stderr, "Cannot block SIGUSR1: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    child = fork();
    if (child == -1) {
        fprintf(stderr, "Cannot fork a child process: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    } else
    if (!child)
        return child_process(getppid(), &mask);
    else
        parent_process(child, &mask);

    printf("Parent: Reaping child process.\n");
    fflush(stdout);

    do {
        p = waitpid(child, &status, 0);
        if (p == -1) {
            if (errno == EINTR)
                continue;
            printf("Parent: waitpid() error: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    } while (p != child);

    if (WIFEXITED(status)) {
        switch (WEXITSTATUS(status)) {
        case EXIT_SUCCESS:
            printf("Parent: Child reaped; EXIT_SUCCESS.\n");
            break;
        case EXIT_FAILURE:
            printf("Parent: Child reaped; EXIT_FAILURE.\n");
            break;
        default:
            printf("Parent: Child reaped; exit status %d.\n", WEXITSTATUS(status));
        }
    } else
    if (WIFSIGNALED(status)) {
        printf("Parent: Child died from signal %d.\n", WTERMSIG(status));
    } else {
        printf("Parent: Child process was lost unexpectedly.\n");
    }

    return EXIT_SUCCESS;
}

Вы можете поэкспериментировать с сигналами и выходным статусом ребенка, так как этот также сообщает об этом.

Чтобы разработать схему пинг-понга сигнала, я предлагаю сначала записать ее в виде временной шкалы.Например:

Child sleeps for a second  ┆  Parent waits for SIGUSR1
Child sends SIGUSR1        ┆
Child waits for SIGUSR2    ┆  Parent receives SIGUSR1
                           ┆  Parent sleeps for a second
                           ┆  Parent sends SIGUSR2
Child receives SIGUSR2     ┆  Parent waits for SIGHUP
Child sends SIGHUP         ┆
Child sleeps for a second  ┆  Parent receives SIGHUP
Child exits                ┆
                           ┆  Parent reaps child
                           ┆  Parent exits

Такая диаграмма помогает писать код, проверять, соответствует ли вывод ожиданиям (отмечая, что не все события упорядочены), и проверять, правильно ли код реализует диаграмму.Это тоже должно быть частью программной документации.

0 голосов
/ 21 декабря 2018

С эта ссылка :

sigwaitinfo() приостанавливает выполнение вызывающего потока, пока один из сигналов в наборе не будет ожидающим (если один из сигналов в наборе равен
уже ожидает обработки для вызывающего потока, sigwaitinfo() немедленно вернет
.)

sigwaitinfo() удаляет сигнал из набора ожидающих сигналов и возвращает номер сигнала в качестве результата его функции.Если аргумент info не является NULL, то буфер, на который он указывает, используется для
возврата структуры type siginfo_t, содержащей
информацию о сигнале.

Если установлено несколько сигналовожидающие для звонящего, сигнал
, который извлекается с помощью s igwaitinfo(), определяется в соответствии с
обычными правилами упорядочения.

Мне было интересно узнать разницу с sigwait издесь это

Функция sigwait() приостанавливает выполнение вызывающего потока, пока один из сигналов, указанных в наборе наборов сигналов, не станет ожидающим.Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала в сигн.

Операция sigwait() такая же, как sigwaitinfo, за исключением
, что:

  • sigwait() возвращает только номер сигнала, а не siginfo_t структуру, описывающую сигнал.

  • Возвращаемые значения двух функцийразличаются.

Синтаксис для функций:

int sigwaitinfo(const sigset_t *set, siginfo_t *info);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...