Синхронизация N родственных процессов после разветвления - PullRequest
0 голосов
/ 12 декабря 2018

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

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/msg.h>

#define NUM_KIDS 4

void handle(int signum);

int main(int argc, char const *argv[])
{
    sem_t* sem;
    sem = sem_open("/ok", O_CREAT, 0);
    signal(SIGUSR1, handle);

    for(int i = 0; i < NUM_KIDS; i++) {
        switch(fork()) {
            case 0:
                fprintf(stderr, "ready %d from %d\n", getpid(), getppid());
                /* i would like that each child stop here untill everyone is ready */
                for(int j = 0; j < 10; j++) 
                fprintf(stderr, "lot of stuff\n");
                exit(0);
            break;
            default:
                /* unleashing the kids when everyone is ready */
                wait(NULL);
                fprintf(stderr, "OK\n");

            break;
        }
    }
    return 0;
}

void handle(int signum) {;}

И я считаю, что вывод должен быть (после синхронизации ребенка)

ready ... from xxx
ready ... from xxx
ready ... from xxx
ready ... from xxx
...lots of stuff... 10 times
...lots of stuff... 10 times
...lots of stuff... 10 times
...lots of stuff... 10 times

1 Ответ

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

Синхронизация

Есть простой трюк:

  • Создайте канал перед тем, как что-то разветвлять.
  • Пусть каждый дочерний элемент закроет конец записи канала.
  • Пусть дети прочитают из канала, когда вы хотите их синхронизировать.
  • Пусть родитель закроет оба конца канала, когда должны быть запущены дочерние элементы.
  • Имейтедети закрывают конец чтения канала, когда освобождаются, чтобы освободить ресурсы.
  • Теперь дети делают «свое дело» (растут, производят продукцию, умирают).
  • Родитель теперь ждет смерти своих детей (это болезненное дело, когда вы играете с процессами в Unix).

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

Если вы хотите, чтобы родитель знал, что все потомки готовы, создайте два конвейера до того, какразветвлятьсяРодительский процесс закрывает конец записи этого второго канала и затем читает с конца чтения.Все дети закрывают оба конца трубы, прежде чем приступить к вызову read() на первой трубе.Родительский процесс получает EOF, когда все дочерние элементы закрывают конец записи канала, поэтому он знает, что все дочерние элементы запущены, по крайней мере, до закрытия второго канала.Затем родитель может закрыть первый канал, чтобы освободить дочерние элементы (и закрыть конец чтения второго канала).

Не ждите слишком рано!

Вы ожидаете в default пункт о переключателе, который не является правильным.Вам нужно запустить все четыре дочерних процесса, прежде чем вы начнете ждать, иначе они никогда не смогут синхронизироваться.Когда вы ждете, вам нужно будет ждать в (новом) цикле.И, во время отладки, вы должны добавить операторы print, чтобы определить, что происходит в родительском процессе.Например, вы напечатаете состояние процессов, которые завершаются, и их PID:

int corpse;
int status;
while ((corpse = wait(&status)) > 0)
    printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);

Рабочий код

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

#define NUM_KIDS 4

int main(void)
{
    int p_pipe[2];
    int c_pipe[2];
    char c;
    if (pipe(p_pipe) != 0 || pipe(c_pipe) != 0)
    {
        fprintf(stderr, "Oops: failed to create pipes\n");
        return 1;
    }

    for (int i = 0; i < NUM_KIDS; i++)
    {
        switch (fork())
        {
        case 0:
            fprintf(stderr, "ready %d from %d\n", (int)getpid(), (int)getppid());
            close(p_pipe[0]);
            close(p_pipe[1]);
            close(c_pipe[1]);
            read(c_pipe[0], &c, 1);
            close(c_pipe[0]);
            for (int j = 0; j < 10; j++)
                fprintf(stderr, "lot of stuff\n");
            return NUM_KIDS + i;
        case -1:
            fprintf(stderr, "failed to fork child %d\n", i+1);
            return 1;
        default:
            break;
        }
    }

    close(p_pipe[1]);
    read(p_pipe[0], &c, 1);
    printf("%d: %d children started\n", (int)getpid(), NUM_KIDS);
    close(c_pipe[0]);
    close(c_pipe[1]);

    int corpse;
    int status;
    while ((corpse = wait(&status)) >= 0)
        printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
    return 0;
}

Пример выполнения

ready 81949 from 81948
ready 81950 from 81948
ready 81951 from 81948
ready 81952 from 81948
81948: 4 children started
lot of stuff
lot of stuff
lot of stuff
lot of stuff
…lines omitted for brevity…
lot of stuff
lot of stuff
lot of stuff
lot of stuff
81948: child 81951 exited with status 0x0600
81948: child 81952 exited with status 0x0700
81948: child 81950 exited with status 0x0500
81948: child 81949 exited with status 0x0400
...