Теперь перейдем к программе, представленной на Рисунке 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