Параллельное и распределенное программирование на С++
Шрифт:
22 int Fd[2] ;
23 float Data;
24 vector<float> X;
25 int NumElements;
26 multiplier N(12.2);
27 strcpy(Value,getenv(«Fdin»));
28 Fd[0] = atoi(Value);
29 strcpy(Value,getenv(«Fdout»));
30 Fd[l] = atoi(Value);
31 ifstream IPipe;
32 ofstream OPipe;
33 IPipe.attach(Fd[0]) ;
34 OPipe.attach(Fd[l]) ;
35 ostream_iterator<float> OPtr(OPipe,"\n»);
36 IPipe » NumElements;
37 for(int N = 0;N < NumElements;N++)
38 {
39 IPipe » Data;
40 X.push_back(Data);
41 }
42 OPipe « X.size « endl;
43 transform(X.begin,X.end,OPtr,N);
44 OPipe « flush;
45 return(0); 46
47 }
Сыновний процесс считывает элементы данных из канала, помещает их в вектор, азатем выполняет математические преобразования над каждым элементом вектора, после чего отправляет их назад
Профиль программы 11.2.1
Имя программы program11-2b.cc
Описание Программа представляет собой код сыновнего процесса, который запускается npoграммой 11.2. В этой программе для получения содержимого контейнера, отправленного из программы 11.2, используется объект класса ifstream. Для отправки через канал обработанной информации родительскому процессу в программе исполь-|зуется объект класса ostream_iterator и стандартный алгоритм transform.
Требуемые заголовки
<iostream>, algorithm>, <fstream>, <vector>, <iterator>, <stdlib.h>, |<string.h>, <unistd.h>.
Инструкции no компиляции и компоновке программ
с++ -o»programll-2b programll-2b.ee
Инструкции по выполнению [Эта программа запускается программой 11.2.
Несмотря на то что классы библиотеки iostream, итераторы типа istream_iterator и ostream_iterator упрощают программирование канала, они не изменяют его поведение. По-прежнему остаются в силе вопросы блокирования и проблемы, связанные с корректным порядком открытия и закрытия каналов, рассмотренные в главе 5. Но использование основных механизмов тех же методов объектно-ориентированного программирования все же позволяет понизить уровень сложности параллельного и распределенного программирования.
FIFO-очереди (именованные каналы),
Методы, которые мы использовали для реализации объектно-ориентированных анонимных каналов, обладают двумя недостатками. Во-первых, любым процессам, которые взаимодействуют с другими процессами, нужен доступ к файловым дескрипторам, возвращаемым при вызове системной функции pipe . Поэтому существует проблема получения этих файловых дескрипторов для всех процессов-участников. Эта проблема легко решается, если процессы связаны отношение м «родитель-потомок» (как в програ мм ах 11.1, 11.2 и 11.2.1), но в это м случае возникает другая проблема. Выходит, во-вторых, что процессы, которые используют неи м енованные каналы, должны быть связаны отношения м и. Это требование можно обойти с помощью схемы передачи дескриптора. Для решения этой проблемы используется структура FIFO (First In — First Out — первым прибыл, первым обслужен). Самое большое ее достоинство как раз и состоит в том, что к ней могут получить доступ процессы, не связанные никакими отношениями. Процессы должны выполняться на одном компьютере — это единственное, что должно их связывать. При этом процессы могут запускаться программами, реализованными на разных языках программирования и с использованием различных парадигм программирования (например, обобщенной или объектно-ориентированной). При групповых вычислениях и при использовании других конфигураций равноправных элементов можно воспользоваться преимуществами FIFO-очередей (иногда называе м ых именованными каналами), поскольку в UNIX- и Linux-среде FIFO-структура имеет имя (определяемое пользователем) и ее (в отличие от анонимных каналов) можно сравнить с «капитальным сооружением». FIFO — однонаправленная структура, а это значит, что пользователь именованного канала в среде UNIX должен открыть его либо для чтения, либо для записи, но не для того и другого одновременно. Именованные каналы, созданные в среде UNIX, остаются в файловой системе до тех пор, пока они не будут явно удалены
Чтобы связать анонимные каналы с объектами классов ifstream и ofstream, мы использовали нестандартное связывание с файловым дескриптором. Нестандартность ситуации вытекает из того, что «брак» между файловыми дескрипторами и iostreams-объектами пока не «освящен» стандартом ISO С++. Поэтому безопаснее использовать FIFO-структуры. К FIFO-файлу специального типа можно получить доступ с помощью имени в файловой системе, в которой «официально» поддерживается связывание с объектами С++-классов ifstream и ofstream. Поэтому точно так же, как мы упрощали межпроцессное взаимодействие (IPC) с помощью iostream-классов и анонимного канала, мы упрощаем доступ к FIFO-структуре. FIFO-структура, основные функции которой совпадают с функциями анонимного канала, позволяет распространить возможности взаимодействия на классы, не связанные никакими родственными отношениями. Однако каждая программа — участник взаимодействия должна при этом «знать» имена FIFO-структур. Это требование, казалось бы, напоминает ограничение, с котороым мы встречались при использовании файловых дескрипторов. Однако FIFO — это все же «шаг вперед». Во-первых, при открытии анонимного канала только система определяет, какие файловые дескрипторы доступны в данный момент. Это означает, что программист не в состоянии полностью контролировать ситуацию. Во-вторых, существует ограничение на количество файловых дескрипторов, котороми располагает система. В-третьих, поскольку FIFO-структурам имена присваиваются пользователем, то количество таких имен не ограничивается. Файловые дескрипторы должны принадлежать файлам, открытым ранее (и причем успешно), а FIFO-имена — это всего лишь имена. FIFO-имя определяется пользователем, а файловые дескрипторы— системой. Имена файлов связываются с объектами классов ifstream, fstream и ofstream с помощью либо конструктора класса либо метода open. В программе 11.3.1 для связывания объектов классов ofstream и ifstream с FIFO-структурой используется конструктор.
// Программа 11.3.1
14 using namespace std;
15
16 const int FMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
17
18 int main(int argc, char *argv[])
19 {
20
21 int Pid,Status,Size;
22 double Value;
25 mkfifo("/tmp/channel.l»,FMode) ;
26 mkfifo (" / tmp/channel. 2», FMode) ;
28 vector<double> X(100,13.0);
29 vector<double> Y;
30 ofstream OPipe("/tmp/channel.l»,ios::app);
31 ifstream IPipe("/tmp/channel.2»);
32 OPipe << X.size « endl;
33 ostream_iterator<double> Optr(OPipe,"\n»);
34 copy(X.begin,X.end,Optr);
35 OPipe « flush;
36 IPipe » Size;
37 for (int N = 0;N < Size; N++)
38 {
39 IPipe » Value;
40 Y.push_back(Value);
41 }
42
43 IPipe.close;
44 OPipe.close;
45 unlink("/tmp/channel.1»);
46 unlink("/tmp/channel.2»);
47 cout « accumulate(Y.begin,Y.end,-13.0) « endl;
48
49 return(0);
50 }
В программе 11.3.1 используется две FIFO-структуры. Вспомните, что FIFO-структуры являются однонаправленными компонентами. Поэтому, если процессы должны обмениваться данными, то необходимо использовать по крайней мере две FIFO-структуры. В программе 11.3.1 они называются channel.1 и channel.2. Обратите внимание на установку флагов полномочий для FIFO-структур (строка 16). Эти полномочия означают, что владелец FIFO-структуры имеет право доступа для чтения и записи, а все остальные — право доступа только для чтения. При выполнении строки 30 FIFO-структура channel.1 будет открыта только для вывода данных. Тот же результат можно было бы получить следующим образом : OPipe. open ("/tmp/channel.1», ios::app);
Используемые здесь параметры алгоритма open означают, что FIFO-структура будет открыта в режиме дозаписи. В программе 11.3.1 алгоритм copy используется для вставки объектов в объект OPipe типа fstream и косвенно в FIFO-структуру. Мы могли бы также использовать здесь объект типа
fstream:fstreamOPipe("/tmp/channel.l», ios::out | ios::app);
В этом случае взаимодействие процессов было бы ограничено выводом данных только в режиме дозаписи. Если бы мы не использовали флаг ios: :app , попытка объекта типа ofstream создать FIFO-сгруктуру (см. строку 30) была бы неудачной.