Номер файлового дескриптораПервоначальноПосле закрытия файлового дескриптора 0После вызова dup
0Стандартный ввод{closed}Файловый дескриптор канала
1Стандартный выводСтандартный выводСтандартный вывод
2Стандартный поток ошибокСтандартный поток ошибокСтандартный поток ошибок
3Файловый дескриптор каналаФайловый дескриптор каналаФайловый дескриптор канала

А теперь выполните упражнение 13.8.

Упражнение 13.3. Каналы и dup

Давайте вернемся к предыдущему примеру, но на этот раз вы измените дочернюю программу, заменив в ней файловый дескриптор stdin концом считывания read созданного вами канала. Вы также выполните некоторую реорганизацию файловых дескрипторов, чтобы дочерняя программа могла правильно определить конец данных в канале. Как обычно, мы пропустили некоторые проверки ошибок для краткости.

Превратите программу pipe3.c в pipe5.c с помощью следующего программного кода:

#include

#include

#include

#include

int main() {

 int data_processed;

 int file pipes[2];

 const char some_data[] = "123";

 pid_t fork_result;

 if (pipe(file_pipes) == 0) {

  fork_result = fork();

  if (fork_result == (pid_t)-1) {

   fprintf(stderr, "Fork failure");

   exit(EXIT_FAILURE);

  }

  if (fork_result == (pid_t)0) {

   close(0);

   dup(file_pipes[0];

   close(file_pipes[0]);

   close(file_pipes[1]);

   execlp("od", "od", "-c", (char*)0);

   exit(EXIT_FAILURE);

  } else {

   close(file_pipes[0]);

   data_processed = write(file_pipes[1], some_data,

    strlen(some_data));

   close(file_pipes[1]);

   printf("%d — wrote %d bytes\n", (int)getpid(), data_processed);

  }

 }

 exit(EXIT_SUCCESS);

}

У этой программы следующий вывод:

$ ./pipe5

22495 - wrote 3 bytes

0000000 1 2 3

0000003

Как это работает

Как и прежде, программа создает канал, затем выполняет вызов fork, создавая дочерний процесс. В этот моменту обоих процессов, родительского и дочернего, есть файловые дескрипторы для доступа к каналу, по одному для чтения и записи, т.е. всего четыре открытых файловых дескриптора.

Давайте первым рассмотрим дочерний процесс. Он закрывает свой стандартный ввод с помощью close(0) и затем вызывает dup(file_pipes[0]). Этот вызов дублирует файловый дескриптор, связанный с концом read канала, как файловый дескриптор 0, стандартный ввод. Далее дочерний процесс закрывает исходный файловый дескриптор для чтения из канала, file_pipes[0]. Поскольку этот процесс никогда не будет писать в канал, он также закрывает файловый дескриптор для записи в канал, file_pipes[1]. Теперь у дочернего процесса единственный файловый дескриптор, связанный с каналом, файловый дескриптор 0, его стандартный ввод.

Далее дочерний процесс может применить exec для вызова любой программы, которая читает стандартный ввод. В данном случае мы используем команду od. Команда od будет ждать, когда данные станут ей доступны, как если бы она ждала ввода с терминала пользователя. В действительности без специального программного кода, позволяющего непосредственно выяснить разницу, она не будет знать, что ввод приходит из канала, а не с терминала.

Родительский процесс начинает с закрытия конца чтения канала, file_pipes[0], потому что он никогда не будет читать из канала. Затем он пишет данные в канал. Когда все данные записаны, родительский процесс закрывает конец записи в канал и завершается. Поскольку теперь нет файловых дескрипторов, открытых для записи в канал, программа od сможет считать три байта, записанных в канал, но последующие операции чтения далее будут возвращать 0 байтов, указывая на конец файла. Когда read вернет 0, программа od завершится. Это аналогично выполнению команды od, введенной с терминала, и последующему нажатию комбинации клавиш + для отправки признака конца файла команде od.

На рис. 13.3 показан результат вызова pipe, на рис. 13.4 — результат вызова fork, а на рис. 13.5 представлена программа, когда она готова к передаче данных.

Рис. 13.3

Рис. 13.4

Рис. 13.5

<p>Именованные каналы: FIFO</p>
Перейти на страницу:

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