26 Pid = fork();
27 if(Pid != 0){
28 ofstream OPipe;
29 OPipe.attach(Fdl[l] ) ,-
30 ostream_iterator
31 OPipe « X.size() « endl;
32 copy(X.begin(),X.end(),OPtr);
33 OPipe « flush;
34 ifstream IPipe;
35 IPipe.attach(Fd2[0]);
36 IPipe » Size;
37 for(int N = 0; N < Size;N++)
38 {
39 IPi ре » Data;
40 Y.push_back(Data);
41 }
42 wait(&Status);
43 ostream_iterator
44 copy(Y.begin(),Y.end(),OPtr2);
45 OPipe.close();
46 IPipe.close();
47 }
48 else{
49 execl("./programll-2b»,«programll-2b»,NULL);
50 } 51
52 return(0);
53 }
В строках 21 и 25 системнал функция setenv () используется для передачи значений файловых дескрипторов сыновнему процессу. Это возможно благодаря тому, что сыновний процесс наслелует среду родительского процесса. Мы можем устанавливать переменные среды в программе с помощью вызова функции setenv (). В данном случае мы устанавливаем их следующим образом.
Fdin=filedesc; Fdout=filedesc;
Сыновний процесс затем использует системный вызов getenv( ) для считывания значений переменных Fdin и Fdout. Значение переменной Fdin будет представлять «считывающий конец» канала для сыновнего процесса, а значение переменной Fdout — «записывающий». Использование системных функций setenv () и getenv() обеспечивает просгую форму межпроцессного взаимодействия (interprocess communication — IPC) между родительским и сыновним процессами. Каналы создаются при выполнении инструкций, приведенных в строке 14. Родительский процесс присоединяется к одному концу канала для операции записи с помощью метода attach() (строка29). После присоединения любые данные, помещенные в объект OPipe типа ofstream, будут записаны в канал. Итератор типа ostream_iterator подключается к объекгу OPipe при выполнении следующей инструкции (строка 30):
ostream_iterator
Теперь итератор OPtr ссылается на объект OPipe. После каждой порции помещаемых в канал данных будет вставляться разделитель "\n». С помощью итератора OPtr мы можем поместить в канал любое количество float -значений. При этом мы можем связать с каналом несколько итераторов различных типов. Но в этом случае необходимо, чтобы на «считывающем» конце канала данные извлекались с использованием ите раторов соответствующих типов. При выполнении слелующей инструкции из программы 11.2 в канал сначала помещается количество элементов, подлежащих передаче: OPipe « X.size() « endl;
Сами элементы отправляются с использованием одного из стандартных С++-алгоритмов:
copy(X.begin() ,X.end() ,OPtr) ;
Алгоритм copy () копирует содержимое одного контейнера в контейнер, связанный с итератором приемника. Здесь итератором приемника является объект OPtr. Объект OPtr связан с объектом OPipe, поэтому при выполнении алгоритма copy () («уместившегося» в одной строке кода) в канал переписывается все содержимое контейнера. Этот пример демонстрирует возможность использования стандартных алгоритмов для организации взаимодействия между различными частями сред параллельного или распределенного программирования. В данном случае алгоритм copy () пересылает информацию от одного процесса другому (из одного адресного пространства в другое). Эти процессы выполняются параллельно, и алгоритм copy () значительно упрощает взаимодействие между ними. Мы подчеркиваем важность этого подхода, поскольку, если есть хоть какал-то возможность упростить логику параллельной или распределенной программы, ею нужно непременно воспользоваться. Ведь межпроцессное взаимодействие — это один из самых сложных разделов параллельного или распределенного программирования. С++-алгоритмы, библиотека классов iostreamS и итератор типа ostream_iterator как раз и позволяют понизить уровень сложности разработки таких программ. Использование манипулятора flush (в строке 33) гарантирует прохождение данных по каналу.
В программе 11.2.1 сыновний процесс сначала получает количество объектов, принимаемых от канала (в строке 36), а затем для считывания самих объектов использует объект IPipe класса istream.
// Программа 11.2.1
11 class multiplier{
12 float X;
13 public:
14 multiplier(float Value) { X = Value;}