Ответ следующий: дублировать файловые дескрипторы, используя методики, описанные в разделе 5.5. Традиционно для достижения желаемого результата выполняется следующая цепочка вызовов:
int pfd[2];
pipe(pfd); /* Выделим для канала (к примеру) файловые дескрипторы 3 и 4 */
/* Здесь выполняем другие шаги, например fork() */
close(STDOUT_FILENO); /* Освобождаем файловый дескриптор 1 */
dup(pfd[1]); /* При дублировании используется наименьший
свободный номер дескриптора, то есть fd 1 */
Конечным результатом вышеприведенных шагов является то, что стандартный вывод процесса привязывается к записывающему концу канала. Аналогичная цепочка вызовов применяется для привязки считывающего конца канала к стандартному вводу процесса.
Стоит отметить: данные шаги основаны на предположении о том, что файловые дескрипторы процесса с номерами 0, 1 и 2 уже открыты (командная оболочка обычно делает это автоматически для каждой программы, которая в ней выполняется). Если бы дескриптор 0 был закрыт до вышеприведенных вызовов, то мы бы ошибочно привязали к записывающему концу канала стандартной
dup2(pfd[1], STDOUT_FILENO); /* Закрываем дескриптор 1 и повторно устанавливаем
связь с записывающим концом канала */
Продублировав pfd[1], мы получили два дескриптора, ссылающихся на записывающий конец канала: дескриптор 1 и pfd[1]. Поскольку неиспользуемые файловые дескрипторы канала следует закрывать, мы сделаем это после вызова dup2():
close(pfd[1]);
Код, показанный выше, требует предварительного открытия стандартного вывода. Предположим, что стандартный ввод/вывод был закрыт перед вызовом pipe(). В этом случае вызов pipe() выделил бы для канала два дескриптора — например, pfd[0] со значением 0 и pfd[1] со значением 1. Следовательно, эквивалентом предыдущих вызовов dup2() и close() был бы такой код:
dup2(1, 1); /* Ничего не делает */
close(1); /* Закрывает единственный дескриптор для записывающего конца канала */
В целях безопасности эти вызовы рекомендуется заключить внутрь инструкции if следующего вида:
if (pfd[1]!= STDOUT_FILENO) {
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
}
Программа, представленная в листинге 44.4, использует методики, описанные в данном разделе для получения того же результата, что и у кода из листинга 44.1. Открыв канал, мы создаем два дочерних процесса. Первый привязывает свой стандартный вывод к записывающему концу канала, после чего выполняет команду ls. Второй привязывает свой стандартный ввод к считывающему концу канала и выполняет команду wc.
Листинг 44.4. Использование канала для соединения команд ls и wc
pipes/pipe_ls_wc.c
#include
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
int pfd[2]; /* Файловые дескрипторы канала */
if (pipe(pfd) == -1) /* Создаем канал */
errExit("pipe");
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Первый потомок выполняет 'ls', записывая результат в канал */
if (close(pfd[0]) == -1) /* Считывающий конец не используется */
errExit("close 1 ");
/* Дублируем стандартный вывод на записывающем конце канала;
закрываем лишний дескриптор */
if (pfd[1]!= STDOUT_FILENO) { /* Проверка на всякий случай */
if (dup2(pfd[1], STDOUT_FILENO) == -1)
errExit("dup2 1");
if (close(pfd[1]) == -1)
errExit("close 2");
}
execlp("ls", "ls", (char *) NULL); /* Записывает в канал */
errExit("execlp ls");
default: /* Родитель выходит из этого блока, чтобы создать следующего потомка */
break;
}
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Второй потомок выполняет 'wc', считывая ввод из канала */
if (close(pfd[1]) == -1) /* Записывающий конец не используется */
errExit("close 3");
/* Дублируем стандартный ввод на считывающем конце канала;
закрываем лишний дескриптор */
if (pfd[0]!= STDIN_FILENO) { /* Проверка на всякий случай */
if (dup2(pfd[0], STDIN_FILENO) == -1)
errExit("dup2 2");
if (close(pfd[0]) == -1)
errExit("close 4");
}
execlp("wc", "wc", "-l", (char *) NULL); /* Читает из канала */
errExit("execlp wc ");
default: /* Родитель выходит из этого блока */
break;
}
/* Родитель закрывает лишние дескрипторы канала и ждет завершения дочерних процессов */
if (close(pfd[0]) == -1)
errExit("close 5");
if (close(pfd[1]) == -1)
errExit("close 6");
if (wait(NULL) == -1)
errExit("wait 1");
if (wait(NULL) == -1)
errExit("wait 2");
exit(EXIT_SUCCESS);
}
pipes/pipe_ls_wc.c
Запустив программу из листинга 44.4, мы увидим следующее:
$ ./pipe_ls_wc
24
$ ls | wc — l
24