Как заставить несколько процессов `fork ()` взаимодействовать с использованием общей памяти?

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

Это вообще возможно? Я не могу понять ...

изменить: этот процесс будет использовать общую память.

Ответов (3)

Такие вещи, как анти-поток, могут немного упростить вам задачу, см. Примеры (в частности, программа поиска ns).

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

  • Общая память.
  • трубы ( popenи тому подобное).
  • Розетки.

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

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

  • значение для хранения переменной и возвращаемое значение.
  • значение для хранения состояния (0 = начало, 1 = переменная готова для дочернего элемента, 2 = переменная снова готова для родительского).

Во всех случаях вам нужен способ, чтобы дети усвоили только свои ценности, а не те, которые предназначены для других детей. Это может быть так же просто, как добавление значения в блок общей памяти для хранения PID дочернего элемента. Все дочерние элементы будут сканировать каждый элемент в блоке, но будут обрабатывать только те, у которых состояние равно 1, а PID является их PID.

Например:

  • Main создает общую память для пяти детей. У каждого элемента есть состояние, PID и значение.
  • Main устанавливает все состояния в "start".
  • Main запускает пять дочерних элементов, которые все прикрепляются к общей памяти.
  • Main хранит все свои PID.
  • Все дочерние элементы начинают сканирование общей памяти на предмет состояния = "готово для ребенка" и их PID.
  • Main помещает в первый элемент (state = "готово для дочернего", PID = pid1, value = 7).
  • Main помещает второй элемент (state = "готово для дочернего", PID = pid5, value = 9).
  • Дочерний pid1 берет первый элемент, меняет значение на 49, устанавливает состояние «готово для родителя»), возвращается к мониторингу.
  • Дочерний pid5 берет второй элемент, меняет значение на 81, устанавливает состояние «готово для родителя»), возвращается к мониторингу.
  • Main принимает ответ pid5, устанавливает это состояние обратно в "start.
  • Main принимает ответ pid1, устанавливает это состояние обратно в "start.

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

Самый мерзкий метод использует vfork() и позволяет различным дочерним элементам перед выходом топтать разные части памяти; затем родитель просто складывает измененные биты памяти.

Крайне не рекомендуется, но это единственный случай, с которым я столкнулся, который vfork() может действительно пригодиться.

Просто для развлечения (своего) я закодировал это:

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

int main(void)
{
    int i;
    int array[5];
    int square[5];
    long sum = 0;

    srand(time(0));
    for (i = 0; i < 5; i++)
    {
        array[i] = rand();
        if (vfork() == 0)
        {
            square[i] = array[i] * array[i];
            execl("/bin/true", "/bin/true", (char *)0);
        }
        else
            wait(0);
    }

    for (i = 0; i < 5; i++)
    {
        printf("in: %d; square: %d\n", array[i], square[i]);
        sum += square[i];
    }
    printf("Sum: %d\n", sum);
    return(0);
}

Это работает. Предыдущая пробная версия с использованием " exit(0) " вместо " execl() " не работала; квадратный массив состоял из нулей. Пример вывода (32-битная компиляция на Solaris 10, SPARC):

in: 22209; square: 493239681
in: 27082; square: 733434724
in: 2558; square: 6543364
in: 17465; square: 305026225
in: 6610; square: 43692100
Sum: 1581936094

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

На странице руководства Solaris для ' vfork()' говорится:

В отличие от функции fork (), дочерний процесс заимствует родительскую память и поток управления до вызова execve () или выхода (либо ненормально, либо путем вызова _exit () (см. Exit (2)). Любые изменения сделанное за это время в любой части памяти дочернего процесса, отражается в родительском процессе при возврате из vfork (). Родительский процесс приостанавливается, пока дочерний процесс использует свои ресурсы.

Это, вероятно, означает, что wait() в моем коде нет необходимости. (Однако попытка упростить код, похоже, заставила его вести себя неопределенно. Это довольно важно, чтобы i не измениться преждевременно; это wait() действительно обеспечивает синхронность. Использование _exit() вместо execl() также, похоже, ломает вещи. Не используйте, vfork() если вы цените свое здравомыслие - или если вы хотите получить какие-либо отметки за домашнее задание.)