Как заставить несколько процессов `fork ()` взаимодействовать с использованием общей памяти?
У меня есть родитель с 5 дочерними процессами. Я хочу отправить случайную переменную каждому дочернему процессу. Каждый дочерний элемент возводит переменную в квадрат и отправляет ее обратно родительскому элементу, а родитель суммирует их все вместе.
Это вообще возможно? Я не могу понять ...
изменить: этот процесс будет использовать общую память.
Ответов (3)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()
если вы цените свое здравомыслие - или если вы хотите получить какие-либо отметки за домашнее задание.)