Выбрать главу

Обратите внимание на то. что в функции writer() родительский процесс принудительно "выталкивает" буфер канала, вызывая функцию fflush(). Без этого строка могла бы ""застрять" в буфере и отправиться в канал только после завершения родительского процесса.

При вызове команды ls | less функция fork() выполняется дважды: один раз — для дочернего процесса ls, второй раз — для дочернего процесса less. Оба процесса наследуют копии дескрипторов канала, поэтому могут общаться друг с другом. О соединении несвязанных процессов речь пойдет ниже, в разделе 5.4.5, "Каналы FIFO".

5.4.3. Перенаправление стандартных потоков ввода, вывода и ошибок

Часто требуется создать дочерний процесс и сделать один из концов канала его стандартным входным или выходным потоком. В этом случае на помощь приходит функция dup2(), которая делает один файловый дескриптор равным другому. Вот как, например, можно связать стандартный входной поток с файлом fd:

dup2(fd, STDIN_FILENO);

Символическая константа STDIN_FILENO представляет дескриптор файла, соответствующий стандартному потоку ввода (значение этого дескриптора равно 0). Показанная функция закрывает входной поток, а затем открывает его под видом файла fd. Оба дескриптора (0 и fd) будут указывать на одну и ту же позицию в файле и иметь одинаковый набор флагов состояния, т.е. дескрипторы станут взаимозаменяемыми.

Программа, представленная в листинге 5.8, с помощью функции dup2() соединяет выходной. Конец канала со входом команды sort.[16] После создания канала программа "делится" функцией fork() на два процесса. Родительский процесс записывает в канал различные строки, а дочерний процесс соединяет выходной конец канала со своим входным потоком, после чего запускает команду sort.

Листинг 5.8. (dup2.c) Перенаправление выходного потока канала с помощью функции dup2()

#include <stdio.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

int main() {

 int fds[2];

 pid_t pid;

 /* Создание канала. Дескрипторы обоих концов канала

    помещаются в массив FDS. */

 pipe (fds);

 /* Создание дочернего процесса. */

 pid = fork();

 if (pid == (pid_t)0) {

  /* Это дочерний процесс. Закрываем копию входного конца

    канала */

  close(fds[1]);

  /* Соединяем выходной конец канала со стандартным входным

     потоком. */

  dup2(fds[0], STDIN_FILENO);

  /* Замещаем дочерний процесс программой sort. */

  execlp("sort", "sort", 0);

 } else {

  /* Это родительский процесс. */

  FILE* stream;

  /* Закрываем копию выходного конца канала. */

  close(fds[0]);

  /* Приводим дескриптор входного конца канала к типу FILE*

     и записываем данные в канал. */

  stream = fdopen(fds[1], "w");

  fprintf(stream, "This is a test.\n");

  fprintf(stream, "Hello, world.\n");

  fprintf(stream, "My dog has fleas.\n");

  fprintf(stream, "This program is great.\n");

  fprintf(stream, "One fish, two fish.\n");

  fflush(stream);

  close(fds[1]);

  /* Дожидаемся завершения дочернего процесса. */

  waitpid(pid, NULL, 0);

 }

 return 0;

}

5.4.4. Функции popen() и pclose()

Каналы часто используются для передачи данных программе, выполняющейся как подпроцесс (или приема данных от нее). Специально для этих целей предназначены функции popen() и pclose(), устраняющие необходимость в вызове функций pipe(), dup2(), exec() и fdopen().

Сравните листинг 5.9 с предыдущим примером (листинг 5.8).

Листинг 5.9. (popen.c) Использование функций popen() и pclose()

#include <stdio.h>

#include <unistd.h>

int main() {

 FILE* stream = popen("sort", "w");

 fprintf(stream, "This is a test.\n");

 fprintf(stream, "Hello, world.\n");

 fprintf(stream, "My dog has fleas\n");

 fprintf(stream, "This program is great.\n");

 fprintf(stream, "One fish, two fish.\n");

 return pclose(stream);

}

Функция popen() создает дочерний процесс, в котором выполняется команда sort. Один этот вызов заменяет вызовы функций pipe(), fork(), dup2() и execlp(). Второй аргумент, "w", указывает на то, что текущий процесс хочет осуществлять запись в дочерний процесс. Функция popen() возвращает указатель на один из концов канала; второй конец соединяется со стандартным входным потоком дочернего процесса. Функция pclose() закрывает входной поток дочернего процесса, дожидается его завершения и возвращает код статуса.

вернуться

16

Команда sort читает строки текста ил стандартного входного потока, сортирует их в алфавитном порядке и записывает в стандартный выходной поток.