Как сделать так, чтобы несколько процессов `fork ()` соединялись, используя разделяемую память? - PullRequest
3 голосов
/ 27 февраля 2009

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

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

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

Ответы [ 3 ]

6 голосов
/ 27 февраля 2009

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

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

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

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

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

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

Например:

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

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

3 голосов
/ 27 февраля 2009

Самый отвратительный метод использует 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(), если вы цените свое здравомыслие - или если вы хотите, чтобы какие-либо оценки для вашей домашней работы.)

0 голосов
/ 27 февраля 2009

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

...