Читаем Архитектура операционной системы UNIX полностью

Теперь перейдем к программе, представленной на Рисунке 7.5, в которой процесс-потомок наследует от своего родителя файловые дескрипторы 0 и 1 (соответствующие стандартному вводу и стандартному выводу). При каждом выполнении системной функции pipe производится назначение двух файловых дескрипторов в массивах to_par и to_chil. Процесс вызывает функцию fork и делает копию своего контекста: каждый из процессов имеет доступ только к своим собственным данным, так же как и в предыдущем примере. Родительский процесс закрывает файл стандартного вывода (дескриптор 1) и дублирует дескриптор записи, возвращаемый в канал to_chil. Поскольку первое свободное место в таблице дескрипторов родительского процесса образовалось в результате только что выполненной операции закрытия (close) файла вывода, ядро переписывает туда дескриптор записи в канал и этот дескриптор становится дескриптором файла стандартного вывода для to_chil. Те же самые действия родительский процесс выполняет в отношении дескриптора файла стандартного ввода, заменяя его дескриптором чтения из канала to_par. И порожденный процесс закрывает файл стандартного ввода (дескриптор 0) и так же дублирует дескриптор чтения из канала to_chil. Поскольку первое свободное место в таблице дескрипторов файлов прежде было занято файлом стандартного ввода, его дескриптором становится дескриптор чтения из канала to_chil. Аналогичные действия выполняются и в отношении дескриптора файла стандартного вывода, заменяя его дескриптором записи в канал to_par. И тот, и другой процессы закрывают файлы, дескрипторы которых возвратила функция pipe — хорошая традиция, в чем нам еще предстоит убедиться. В результате, когда родительский процесс переписывает данные в стандартный вывод, запись ведется в канал to_chil и данные поступают к порожденному процессу, который считывает их через свой стандартный ввод. Когда же порожденный процесс пишет данные в стандартный вывод, запись ведется в канал to_par и данные поступают к родительскому процессу, считывающему их через свой стандартный ввод. Так через два канала оба процесса обмениваются сообщениями.

#include ‹string.h›

char string[] = "hello world";

main() {

 int count, i;

 int to_par[2], to_chil[2];

 /* для каналов родителя и потомка */

 char buf[256];

 pipe(to_par);

 pipe(to_chil);

 if (fork() == 0) {

  /* выполнение порожденного процесса */

  close(0);  /* закрытие прежнего стандартного ввода */

  dup(to_chil[0]);  /* дублирование дескриптора чтения из канала в позицию стандартного ввода */

  close(1); /* закрытие прежнего стандартного вывода */

  dup(to_par[0]); /* дублирование дескриптора записи в канал в позицию стандартного вывода */

  close(to_par[1]); /* закрытие ненужных дескрипторов  канала */

  close(to_chil[0]);

  close(to_par[0]);

  close(to_chil[1]);

  for (;;) {

   if ((count = read(0, buf, sizeof(buf))) == 0) exit();

   write(1, buf, count);

  }

 }  /* выполнение родительского процесса */

 close(1); /* перенастройка стандартного ввода-вывода */

 dup(to_chil[1]);

 close(0);

 dup(to_par[0]);

 close(to_chil[1]);

 close(to_par[0]);

 close(to_chil[0]);

 close(to_par[1]);

 for (i = 0; i ‹ 15; i++) {

  write(1, string, strlen(string));

  read(0, buf, sizeof(buf));

 }

}

Рисунок 7.5. Использование функций pipe, dup и fork

Перейти на страницу:

Все книги серии Серия книг по программному обеспечению издательства prentice hall

Похожие книги