Последовательные сигналы между двумя процессами

У меня есть родительский процесс и двое детей. Родительский процесс создает только двух детей, читателя и счетчика, и ожидает его смерти. Дети делают следующие вещи.

Первый ребенок (читатель):

  1. открывает файл,
  2. читает строку,
  3. отправляет сигнал (SIGUSR1) второму ребенку,
  4. ждет сигнала от второго ребенка,
  5. перейдите к 2, если мы можем прочитать другую строку, иначе убить второго ребенка.

Второй ребенок (счетчик):

  1. ждет сигнала (SIGUSR1) от считывателя,
  2. считает длину строки,
  3. отправляет сигнал читателю и переходит к 1.

У меня проблемы с ожиданием сигнала от другого процесса. Сигнал можно получить до того, как я позвоню pause() функция, т. е. процесс может быть заблокирован навсегда. Я также пытался использовать sigprocmask(), sigsuspend() а также sigwaitinfo(), но это не работает правильно.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>

enum { MAX_LEN = 16 };

void handler(int signo)
{
    // do nothing
}

int main(int argc, const char * argv[])
{
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGUSR1);

    // handle SIGUSR1
    signal(SIGUSR1, handler);

    // shared memory for file line
    char *data = mmap(NULL, MAX_LEN, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

    pid_t *pid_counter = mmap(NULL, sizeof(*pid_counter), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

    pid_t pid_reader;
    if (!(pid_reader = fork())) {
        sigprocmask(SIG_BLOCK, &ss, NULL);
        printf("READER: waiting signal from COUNTER\n"); fflush(stdout);
        sigwaitinfo(&ss, NULL);
        sigprocmask(SIG_UNBLOCK, &ss, NULL);
        printf("READER: got signal\n"); fflush(stdout);

        printf("READER: opening file\n"); fflush(stdout);
        FILE *f = fopen(argv[1], "r");
        while (fgets(data, MAX_LEN, f) > 0) {
            printf("READER: reading line and waiting signal from COUNTER\n"); fflush(stdout);

            sigprocmask(SIG_BLOCK, &ss, NULL);
            kill(*pid_counter, SIGUSR1);
            sigwaitinfo(&ss, NULL);
            sigprocmask(SIG_UNBLOCK, &ss, NULL);

            printf("READER: got signal\n"); fflush(stdout);
        }
        printf("READER: closing file and killing COUNTER\n"); fflush(stdout);
        fclose(f);
        kill(*pid_counter, SIGTERM);
        _exit(0);
    }

    if (!(*pid_counter = fork())) {
        sleep(1);
        printf("COUNTER: send signal to READER that COUNTER is ready\n"); fflush(stdout);
        while (1) {
            sigprocmask(SIG_BLOCK, &ss, NULL);
            kill(pid_reader, SIGUSR1);
            printf("COUNTER: waiting signal from READER\n"); fflush(stdout);
            sigwaitinfo(&ss, NULL);
            sigprocmask(SIG_UNBLOCK, &ss, NULL);
            printf("COUNTER: got signal\n"); fflush(stdout);

            printf("%d\n", strlen(data));
            fflush(stdout);
        }
    }

    wait(NULL);
    wait(NULL);

    return 0;
}

Для этого кода я могу получить следующую последовательность:

$ gcc -o prog prog.c && ./prog input.dat
READER: waiting signal from COUNTER
COUNTER: send signal to READER that COUNTER is ready
COUNTER: waiting signal from READER
READER: got signal
READER: opening file
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: closing file and killing COUNTER

Почему счетчик не пишет "получил сигнал"? Как я могу отправлять и получать сигналы синхронно? (Я знаю о других методах IPC, но мне нужна синхронизация через сигналы.)

1 ответ

Решение

Здесь есть несколько очевидных проблем.

То, что на самом деле вызывает вашу проблему, это:

if (!(*pid_counter = fork())) {

Помните, что (1) pid_counter указывает на общую память; и (2) fork() возвращается дважды при каждом вызове. Когда он вернется в ребенка, он установит *pid_counter в 0, но когда он вернется в родительский, он установит *pid_counter ПИД ребенка. Вы не можете предсказать, что произойдет первым. Что в действительности происходит в вашем случае, так это то, что он в конечном итоге настроен на 0и так все ваши kill вызовы в процессе считывания отправляют сигналы каждому процессу в вашей группе процессов, поэтому и считыватель, и счетчик получают их одновременно. Это приводит к сбою синхронизации, потому что оба процесса возвращаются из sigwaitinfo() в то же время. В ридер, вы должны отправить SIGUSR1 только встречный процесс.

Что вам нужно сделать, это изменить это на:

pid_t temp_pid
if ( !(temp_pid = fork()) ) {
    *pid_counter = getpid();

Другие моменты:

  1. sigprocmask() сохраняется через fork() звонки, поэтому вы должны просто установить его один раз, прежде чем fork()ING. Вам никогда не нужно разблокировать его в вашем случае, и не должны.

  2. Нет необходимости (и, возможно, лучше не устанавливать) обработчик для SIGUSR1 если ты звонишь sigwaitinfo() в теме.

  3. strlen() возвращает тип size_t, Так что ваши printf() спецификатор формата должен быть %zuне %d,

  4. Все ваши звонки на fflush(stdout), хотя и безвредны, здесь излишни.

  5. Вы вряд ли когда-либо проверяете какие-либо из ваших системных вызовов. Это не только для производственного кода - если ваша программа не работает, первым делом убедитесь, что вы проверяете все системные вызовы на успешность, потому что причина, по которой она может не работать, заключается в том, что один из этих вызовов возможно, потому что вы передали ему плохую информацию. Более важно проверять успешность, когда вы тестируете свою программу, не менее важно.

В любом случае, вот рабочая версия вашей программы:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>

enum { MAX_LEN = 16 };

int main(int argc, const char * argv[])
{
    if ( argc != 2 ) {
        fprintf(stderr, "Enter one argument, and one argument only.\n");
        return EXIT_FAILURE;
    }

    // shared memory for file line
    char *data = mmap(NULL, MAX_LEN, PROT_READ | PROT_WRITE, MAP_SHARED |
                      MAP_ANON, -1, 0);
    if ( data == MAP_FAILED ) {
        perror("mmap() failed for data");
        exit(EXIT_FAILURE);
    }

    pid_t *pid_counter = mmap(NULL, sizeof *pid_counter, PROT_READ |
                              PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
    if ( pid_counter == MAP_FAILED ) {
        perror("mmap() failed for pid_counter");
        exit(EXIT_FAILURE);
    }

    pid_t pid_reader, temp_pid;

    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGUSR1);
    if ( sigprocmask(SIG_BLOCK, &ss, NULL) == -1 ) {
        perror("sigprocmask() failed");
        exit(EXIT_FAILURE);
    }

    if ( (pid_reader = fork()) == -1 ) {
        perror("fork() failed for reader");
        exit(EXIT_FAILURE);
    }
    else if ( !pid_reader ) {
        printf("READER: waiting signal from COUNTER\n");

        if ( sigwaitinfo(&ss, NULL) == -1 ) {
            perror("sigwaitinfo() failed in reader");
            _exit(EXIT_FAILURE);
        }

        printf("READER: got signal\n");

        printf("READER: opening file\n");

        FILE *f = fopen(argv[1], "r");
        if ( !f ) {
            fprintf(stderr, "Couldn't open input file\n");
            _exit(EXIT_FAILURE);
        }

        while ( fgets(data, MAX_LEN, f) ) {
            printf("READER: reading line and waiting signal from COUNTER\n");

            if ( kill(*pid_counter, SIGUSR1) == -1 ) {
                perror("kill() for SIGUSR1 failed in reader");
                _exit(EXIT_FAILURE);
            }

            if ( sigwaitinfo(&ss, NULL) == -1 ) {
                perror("sigwaitinfo() failed in reader");
                _exit(EXIT_FAILURE);
            }

            printf("READER: got signal\n");
        }
        printf("READER: closing file and killing COUNTER\n");
        fclose(f);
        if ( kill(*pid_counter, SIGTERM) == -1 ) {
            perror("kill() for SIGTERM failed in reader");
            _exit(EXIT_FAILURE);
        }

        _exit(EXIT_SUCCESS);
    }

    if ( (temp_pid = fork()) == -1 ) {
        perror("fork() failed for counter");
        exit(EXIT_FAILURE);
    }
    else if ( temp_pid == 0 ) {
        *pid_counter = getpid();

        sleep(1);
        printf("COUNTER: send signal to READER that COUNTER is ready\n");

        while (1) {
            if ( kill(pid_reader, SIGUSR1) == -1 ) {
                perror("kill() failed for SIGUSR1 in counter");
                _exit(EXIT_FAILURE);
            }

            printf("COUNTER: waiting signal from READER\n");

            if ( sigwaitinfo(&ss, NULL) == -1 ) {
                perror("sigwaitinfo() failed in counter");
                _exit(EXIT_FAILURE);
            }

            printf("COUNTER: got signal\n");

            printf("%zu\n", strlen(data));
        }

        _exit(EXIT_SUCCESS);
    }

    if ( wait(NULL) == -1 ) {
        perror("first wait() failed");
        exit(EXIT_FAILURE);
    }

    if ( wait(NULL) == -1 ) {
        perror("second wait() failed");
        exit(EXIT_FAILURE);
    }

    return 0;
}

со следующим выводом для показанного файла:

paul@thoth:~/src$ cat file.txt
line one
line two
line three

paul@thoth:~/src$ ./sig file.txt
READER: waiting signal from COUNTER
COUNTER: send signal to READER that COUNTER is ready
COUNTER: waiting signal from READER
READER: got signal
READER: opening file
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
9
COUNTER: waiting signal from READER
READER: got signal
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
9
COUNTER: waiting signal from READER
READER: got signal
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
11
COUNTER: waiting signal from READER
READER: got signal
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
1
COUNTER: waiting signal from READER
READER: got signal
READER: closing file and killing COUNTER
paul@thoth:~/src$ 
Другие вопросы по тегам