Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Хьюз Камерон. Параллельное и распределенное программирование на С++ - royallib.ru.doc
Скачиваний:
117
Добавлен:
11.03.2016
Размер:
1.97 Mб
Скачать

Связь каналов c iostream-объектами с помощью дескрипторов файлов

Существует три iostream-класса (ifstream, ofstream и fstream), которые мы можем использовать для подключения к каналу. Объект класса ifstream используется для ввода данных, объект класса ofstream — для их вывода, а объект класса fstream можно применять и в том и в другом случае. Несмотря на то что непосредственная поддержка дескрипторов файлов и потоков ввода-вывода не является частью стандарта ISO, в большинстве UNIX- и Linux-сред поддерживается С++-ориентированный iostream-доступ к дескрипторам файлов. В библиотеке GNU С++ iostreams предусмотрена поддержка дескриптора файла в одном из конструкторов классов ifstream, ofstream и fstream и в методе attach( ) , определенном в классах ifstream и ofstream. UNIX-компилятор языка С++ ко м пании Sun также поддерживает дескрипторы файлов с помощью одного из конструкторов классов ifstream, ofstream и fstream. Поэтому при выполнении следующего фрагмента кода

//...

int Fd[2];

Pipe(Fd);

ifstream IPipe(Fd[0]) ;

ofstream OPipe(Fd[1]) ;

будут созданы объектно-ориентированные каналы. Объект IPipe будет играть роль входного потока, а объект OPipe— выходного. После создания эти потоки можно применять для связи между параллельно выполняемыми процессами с использованием потоково г о представления и операторов вставки (<<) и извлечения (>>). Для С++-сред, которые поддерживают метод attach(), дескриптор файла можно связать с объектами классов ifstream, ofstream или fstream, используя следующий синтаксис.

// Листинг 11.20. Создание канала и использование

// функции attach()

int Fd[2];

ofstream OPipe;

//.. .

pipe(Fd);

//.. .

OPipe.attach(Fd[1]);

//.. .

OPipe << Value << endl;

Такой способ использования объектно-ориентированных каналов предполагает существование сыновнего процесса, который может считывать из них информацию. В программе 11.1 для создания двух процессов используется fork-инструкция. Родительский процесс отправляет значение сыновнему процессу с помощью iostreams-ориентированного канала.

// Программа 11.1

1   #include <unistd.h>

2   #include <iostream.h>

3   #include <fstream.h>

4   #include <math.h>

5   #include <sys/wait.h> 6

7 8 9

10   int main(int argc, char *argv[])

11   {

12

13   int Fd[2];

14   int Pid;

15   float Value;

16   int Status;

17   if(pipe(Fd) != 0) {

18 Cerr « «Ошибка при создании канала " « endl;

19   exit(l);

20   }

21   Pid = fork();

22   if(Pid == 0){

23   ifstream IPipe(Fd[0]);

24   IPipe » Value;

25   cout « «От процесса-родителя получено значение» << Value << endl;

26   IPipe.close();

27   }

28   else{

29   ofstream OPipe(Fd[l]);

30   OPipe « M_PI « endl;

31   wait(&Status);

32   OPipe.close();

33

34 }

35

36 }

Вспомните, что значение 0, возвращаемое функцией fork(), принадлежит сыновнему процессу. В программе 11.1 канал создается при выполнении инструкции, расположенной на строке 17. А при выполнении инструкции, расположенной на строке 29, родительский процесс открывает канал для записи. Файловый дескриптор Fd[1] означает «записывающий» конец канала. К этому концу канала (благо д аря вызову конструктора на строке 29) присоединяется объект класса ofstream. К «считывающему» концу канала присоединяется объект класса ifstream (строка 23). Сыновний процесс открывает канал для чтения и получает доступ к дескриптору файла, поскольку он вместе со средой родителя наслелует и дескрипторы файлов. Таким образом, любые файлы, которые открыты в среде родителя, будут оставаться открытыми и в среде наследника, если операционнал система не получит явные инструкции, основанные на системной функции fcntl. Помимо наследования открытых файлов, маркеры внутрифайловых позиций остаются там, где они были в момент порождения сыновнего процесса, чтобы сыновний процесс также получил доступ к маркеру позиции. При изменении позиции в родительском процессе маркер сыновнего также смещается. В этом случае мы могли бы реализовать потоковое представление данных, не создавал интерфейсный класс. Просто присоединив файловые дескрипторы канала к объектам классов ofstream и ifstream, мы сможем использовать операторы вставки (<<) и извлечения (»). Аналогично любой класс, в котором определены операторы ">>" и "<<", может выполнять операции вставки данных в канал и извлечения их оттуда без какого-либо дополнительного программирования. В программе 11.1 родительский процесс поме щ ает значение M_PI в канал (строка 30), а сыновний процесс извлекает это з н ачение из канала, используя оператор ">>" (строка24). Инструкции по выполнению и компиляции этой программы приведены в разделе «Профиль программы 11.1».

f

(Профиль программы 11.1

Имя программы program11-1.cc

Оп и сание

Программа 11.1 демонстрирует использование объектно-ориентированного потока c использованием анонимных системных каналов. Для создания двух процессов, |которые взаимодействуют между собой с помощью операторов вставки («) и из-!влечения (»), программа использует функцию fork().

Требуемые заголовки

<wait.h>,<unistd.h>, <iostream.h>, <fstream.h>, <math.h>.

Инс т рукци и по компиляции и компоновке программ

C++ -о program11-1 program11-1.cc

Среда для т ес ти рова ни я

Solaris 8, SuSE Linux 7.1.

Инструкции по выполнению

./program11-1

Компилятор gnu С++ также под д ерживает метод attach (). Этот мето д можно использовать д ля связи файловых д ескрипторов с объекта м и классов ifstream и ofstream (листинг 11.21).

// Листинг 11.21. Подключение файловых дескрипторов к

// объекту класса ofstream

int main (int argc, char *argv[]) {

int Fd[2];

ofstream Out;

pipe(Fd);

Out.attach(Fd[l]); // - . .

// Межпроцессное взаимодействие. //. . .

Out.close( );

}

При вызове функции Out.attach(Fd[1] ) объект класса ofstream связывается с файловым дескриптором канала. Теперь Любая информация, которая будет помещена в объект Out, в действительности запишется в канал. Использование операторов извлечения и вставки для выполнения автоматического преобразования формата является основным достоинством использования семейства fstream -классов в сочетании с канальной связью. Возможность применять пользовательские средства извлечения и вставки избавляет программиста от определенных трудностей, которые могут иметь место при программировании каналов связи. Поэтому вместо явного перечисления размеров данных, записываемых в канал и читаемых из него, при управлении доступом для чтения-записи мы используем только количество передаваемых через канал элементов, что существенно упрощает весь процесс. К тому же такое «снижение себестоимости» немного упрощает параллельное программирование. Рекоменлуемый нами метод состоит в использовании архитектуры, в основе которой лежит принцип «разделяй и властвуй». Главное — правильно расставить компоненты «по своим местам» — и программирование станет более простым. Например, поскольку канал связывается с объектами классов ofstream и ifstream, мы можем использовать информацию, хранимую компо н ентом ios, для определения состояния канала. Компоненты преобразования iostreams-классов можно использовать для выполнения автоматического преобразования данных, помещаемых в один конец канала и извлекаемых из его другого конца. Использование каналов вместе с iostream-классами также позволяет программисту интегрировать стандартные контейнеры и алгоритмы с использованием межпроцессного взаимодействия на основе канала. На рис. 11.9 показаны взаимоотношения между объектами классов ifstream, ofstream, каналом и средствами вставки и извлечения при организации межпроцессного взаимодействия.

Для чтения данных из канала и записи данных в канал можно также испо л ьзовать семейство к л ассов fstream и функции-ч л ены read () и write ().