Как синхронизировать дочерние процессы друг с другом с помощью семафоров?

У меня есть N количество дочерних элементов, которые должны выполнять некоторую работу в цикле, одновременно синхронизируясь друг с другом. А именно, если дочерний процесс находится на i-й итерации, все остальные дочерние процессы должны быть на i-й итерации. Мне нужно синхронизировать их с семафорами, но я не могу найти, как это сделать. Это код, который я написал:

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

void sem_signal(int semid, int val) {
    struct sembuf semaphore;
    semaphore.sem_num = 0;
    semaphore.sem_op = val;
    semaphore.sem_flg = 0;
    semop(semid, &semaphore, 1);
}

void sem_wait(int semid, int val) {
    struct sembuf semaphore;
    semaphore.sem_num = 0;
    semaphore.sem_op = (-1 * val);
    semaphore.sem_flg = 0;
    semop(semid, &semaphore, 1);
}

int main() {
    int sem_worker = semget(1, 1, 0700 | IPC_CREAT);
    semctl(sem_worker, 0, SETVAL, 0);
    int process_index = 0;
    int N = 4, pid;

    for (process_index = 0; process_index < N; process_index++) {
        pid = fork();
        if (pid == -1) {
            printf("ERROR: cannot fork!\n");
            return EXIT_FAILURE;
        }
        if (pid == 0)
            break;
    }
    if (pid!=0) // parent
        pause();

    else {
        int i = 0;
        while (i < 3) {
            printf("process %d: i: %d\n", process_index, i);
            sem_signal(sem_worker, 1); // increase the semaphore by one
            sem_wait(sem_worker, N);   // wait for all the other childs
            i += 1;
        }
    }
}

Но когда я запускаю его, он не может продолжаться после первой итерации.

process 0: i: 0
process 1: i: 0
process 3: i: 0
process 2: i: 0
process 0: i: 1

Я понимаю, почему это происходит. Это потому, что один из процессов делает семафор 0 и переходит к следующей итерации, но все остальные все еще ждут. Итак, как мне написать свой код, чтобы решить эту проблему?

P.S. Функции sem_signal и sem_wait я взял откуда-то еще, поэтому не знаю, как они работают, но уверен, что они работают правильно. Например, если я пишу sem_wait(my_sem, num_of_children) в родительском для ожидания всех дочерних процессов и увеличиваю my_sem на 1 в дочерних процессах, когда они заканчиваются, это работает.


person Asocia    schedule 03.06.2020    source источник
comment
Ваш код не имеет никакого смысла. Однако то, что вы описываете, является барьером, вы сможете найти ресурсы, которые помогут вам в этом.   -  person EOF    schedule 03.06.2020
comment
Ну, я написал это только для того, чтобы продемонстрировать свою цель, я знал, что это не сработает. Если вы говорите о функциях выше main, то я их не автор :D Спасибо за предложение, я рассмотрю его.   -  person Asocia    schedule 03.06.2020
comment
@EOF Кажется, это совершенно другая концепция. Это часть моего задания, и там сказано, что вы должны использовать семафоры для синхронизации процессов, поэтому я не могу использовать барьеры. У вас есть другая идея?   -  person Asocia    schedule 03.06.2020
comment
Нет, это не другое понятие. Если вы читаете описание, например. pthread_barrier_wait(), вы видите, что это точно то, что вы необходимость. Вы можете синтезировать барьер из пары семафоров, см. эту свободно доступную книгу< /а>   -  person EOF    schedule 03.06.2020


Ответы (1)


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

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>


typedef struct {
    int n;
    int count;
    sem_t mutex;
    sem_t turnstile;
    sem_t turnstile2;
} barrier_t;

void init_barrier(barrier_t *barrier, int n)
{
    barrier->n = n;
    barrier->count = 0;
    sem_init(&barrier->mutex, 1, 1); // second parameter is pshared
    sem_init(&barrier->turnstile, 1, 0);
    sem_init(&barrier->turnstile2, 1, 0);
}

void phase1(barrier_t *barrier)
{
    sem_wait(&barrier->mutex);
    if (++barrier->count == barrier->n) {
        int i;
        for (i = 0; i < barrier->n; i++) {
            sem_post(&barrier->turnstile);
        }
    }
    sem_post(&barrier->mutex);
    sem_wait(&barrier->turnstile);
}

void phase2(barrier_t *barrier)
{
    sem_wait(&barrier->mutex);
    if (--barrier->count== 0) {
        int i;
        for (i = 0; i < barrier->n; i++) {
            sem_post(&barrier->turnstile2);
        }
    }
    sem_post(&barrier->mutex);
    sem_wait(&barrier->turnstile2);
}

void wait_barrier(barrier_t *barrier)
{
    phase1(barrier);
    phase2(barrier);
}

int shmid, KEYSHM=123456;

int main(int argc, char const* argv[]) {
    barrier_t* barrier;
    shmid = shmget(KEYSHM, sizeof(barrier_t), 0700 | IPC_CREAT);
    barrier = (barrier_t*) shmat(shmid, 0, 0);
    int N = 4;
    init_barrier(barrier, N);
    shmdt(barrier);


    int process_index, pid;

    for (process_index = 0; process_index < N; process_index++) {
        pid = fork();
        if (pid == -1) {
            printf("ERROR: cannot fork!\n");
            return EXIT_FAILURE;
        }
        if (pid == 0)
            break;
    }
    if (pid != 0) // parent
        pause();
    else {
        int i = 0;
        while (i < 3) {
            barrier = (barrier_t*) shmat(shmid, 0, 0);
            printf("process %d: i: %d\n", process_index, i);
            i += 1;
            wait_barrier(barrier);
            shmdt(barrier);
        }

        if (process_index == 3){
            kill(getppid(), SIGKILL);
        }
    }
}
process 0: i: 0
process 1: i: 0
process 2: i: 0
process 3: i: 0
process 2: i: 1
process 3: i: 1
process 0: i: 1
process 1: i: 1
process 3: i: 2
process 2: i: 2
process 0: i: 2
process 1: i: 2
person Asocia    schedule 15.07.2020