Что не так с sigwait во время ожидания сигналов от нескольких процессов - PullRequest
0 голосов
/ 22 апреля 2019

У меня мультипроцесс в этом случае 3, 1 родитель, 2 ребенка.Я ожидаю, что каждый ребенок будет продолжать после возвращения из своих обработчиков сигналов.Но иногда они застревают, иногда только один из них продолжается.В чем моя ошибка?На самом деле я пытаюсь сделать что-то похожее на пробуждение с сигналом.У меня может быть больше сигнала, чтобы сделать это, но что, если нужно 100 детей?Итак, я хочу добиться этого только с помощью SIGUSR2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>


void tellerHandler(int sig) {
    //write(STDERR_FILENO, "Teller has caught SIGUSR2 signal\n", 33);
    printf("pid %u Teller has caught SIGUSR2 signal\n", getpid());
}

int main() {

    int NUM_OF_CHILDREN = 2;
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_flags = 0;
    sa.sa_handler = tellerHandler;
    if (sigaction(SIGUSR2, &sa, NULL) == -1) {
        perror("sigaction error");
        exit(-1);
    }

    sigset_t new_mask;
    sigfillset(&new_mask);
    sigdelset(&new_mask, SIGUSR2);

    int returnedPid = -1;
    pid_t pidList[NUM_OF_CHILDREN];

    for (int i = 1; i < 1 + NUM_OF_CHILDREN; ++i) {
        if ((returnedPid = fork()) == 0) {
            break;
        } else {
            pidList[i - 1] = returnedPid;
        }
    }

    if (returnedPid == 0) {
        sigsuspend(&new_mask);
        printf("child %u returned from handler\n", getpid());
    } else {
        for (int i = 0; i < NUM_OF_CHILDREN; ++i) {
            printf("child %u\n", pidList[i]);
            kill(pidList[i], SIGUSR2);
        }

        for (int i = 0; i < NUM_OF_CHILDREN; ++i) {
            waitpid(pidList[i], 0, 0);
        }
        puts("parent exiting...\n");
    }

    puts("donee");
}

Несколько разных выходов с несколькими прогонами,

child 66313
child 66314
pid 66313 Teller has caught SIGUSR2 signal
child 66313 returned from handler
pid 66314 Teller has caught SIGUSR2 signal

---

child 66330
child 66331
pid 66330 Teller has caught SIGUSR2 signal
pid 66331 Teller has caught SIGUSR2 signal

---

Когда я увеличиваю число детей до 3, следующий вывод будет похож на

child 66738
child 66739
child 66740
pid 66739 Teller has caught SIGUSR2 signal
pid 66738 Teller has caught SIGUSR2 signal
child 66738 returned from handler
donee
pid 66740 Teller has caught SIGUSR2 signal

1 Ответ

0 голосов
/ 22 апреля 2019

Ваша основная проблема здесь - это состояние состязания между родителем, посылающим сигнал SIGUSR2, и потомком, вызывающим sigsuspend. Если дочерний процесс запускается медленно (er) и родитель запускается первым, он может отправить сигнал ДО того, как дочерний элемент вызовет sigsuspend. Поскольку дочерний процесс запускается (возвращается из fork ()) с активным и немаскированным обработчиком сигнала, он может сразу же поймать сигнал (и напечатать сообщение о его перехвате), а затем вернуться и вызвать sigsuspend. Поскольку к этому моменту сигнал уже обработан, sigsuspend будет ожидать второй сигнал, который никогда не поступит.

Исправление состоит в том, чтобы гарантировать, что SIGUSR2 заблокирован в дочернем процессе, пока он не вызовет sigsuspend. Поместите код, чтобы сделать это ДО цикла, который вызывает fork:

sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR2);
sigprocmask(SIG_BLOCK, &ss, 0);
...