Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
Строки 72–83 делают подобные же шаги для правого потомка. Вот что происходит при запуске:
$ <b>ch09-pipeline</b> /* Запуск программы */
left child terminated, status: 0 /* Левый потомок завершается до вывода (!) */
hello there /* Вывод от правого потомка */
right child terminated, status: 0
$ <b>ch09-pipeline</b> /* Повторный запуск программы */
hello there /* Вывод от правого потомка и ... */
right child terminated, status: 0 /* Правый потомок завершается до левого */
left child terminated, status: 0
Обратите внимание, что порядок, в котором завершаются потомки, не является детерминированным. Он зависит от загрузки системы и многих других факторов, которые могут повлиять на планирование процессов. Вам следует проявить осторожность, чтобы избежать предположений о порядке действий при написании кода, создающего несколько процессов, в особенности для кода, который вызывает семейство функций wait().
Весь процесс показан на рис. 9.5.
![](https://cdn.pochitay-knigi.com/s20/3/3/1/8/1/3/img/img_18.jpg)
![](https://cdn.pochitay-knigi.com/s20/3/3/1/8/1/3/img/img_19.jpg)
![](https://cdn.pochitay-knigi.com/s20/3/3/1/8/1/3/img/img_20.jpg)
Рис. 9.5. Создание конвейера родителем
На рис. 9.5 (а) изображена ситуация после создания родителем канала (строки 22–25) и двух порожденных процессов (строки 27–37).
На рис. 9.5 (b) показана ситуация после закрытия родителем канала (строки 39–40) и начала ожидания порожденных процессов (строки 42–50). Каждый порожденный процесс поместил канал на место стандартного вывода (левый потомок, строки 61–63) и стандартного ввода (строки 76–78).
Наконец, рис. 9.5 (с) изображает ситуацию после закрытия потомками первоначального канала (строки 64 и 79) и вызова execvp() (строки 66 и 81).
9.4.2. Создание нелинейных конвейеров: /dev/fd/XX
Многие современные системы Unix, включая GNU/Linux, поддерживают в каталоге /dev/fd[98] специальные файлы. Эти файлы представляют дескрипторы открытых файлов с именами /dev/fd/0, /dev/fd/1 и т.д. Передача такого имени функции open() возвращает новый дескриптор файла, что в сущности является тем же самым, что и вызов dup() для данного номера дескриптора.
Эти специальные файлы находят свое применение на уровне оболочки: Bash, ksh88 (некоторые версии) и ksh93 предоставляют возможность замещения процесса (process substitution), что позволяет создавать нелинейные конвейеры. На уровне оболочки для входного конвейера используется запись '<(...)', а для выходного конвейера запись '>(...)'. Например, предположим, вам нужно применить команду diff к выводу двух команд. Обычно вам пришлось бы использовать временные файлы:
command1 > /tmp/out.$$.1
command2 > /tmp/out.$$.2
diff /tmp/out.$$.1 /tmp/out.$$.2
rm /tmp/out.$$.1 /tmp/out.$$.2
С замещением процессов это выглядит следующим образом:
diff <(command1) <(command2)
Не надо никаких беспорядочных файлов для временного запоминания и удаления. Например, следующая команда показывает, что наш домашний каталог является ссылкой на другой каталог:
$ <b>diff <(pwd) <(/bin/pwd)</b>
1c1
< /home/arnold/work/prenhall/progex
---
> /d/home/arnold/work/prenhall/progex
Незамысловатая команда pwd является встроенной в оболочку: она выводит текущий логический путь, который управляется оболочкой с помощью команды cd. Программа /bin/pwd осуществляет обход физической файловой системы для вывода имени пути.
Как выглядит замещение процессов? Оболочка создает вспомогательные команды[99] ('pwd' и '/bin/pwd'). Выход каждой из них подсоединяется к каналу, причем читаемый конец открыт в дескрипторе нового файла для главного процесса ('diff'). Затем оболочка передает главному процессу имена файлов в <i>/dev/fd</i> в качестве аргументов командной строки. Мы можем увидеть это, включив в оболочке трассировку исполнения.
$ <b>set -х</b> /* Включить трассировку исполнения */
$ <b>diff <(pwd) <(/bin/pwd)</b> /* Запустить команду */
+ diff /dev/fd/63 /dev/fd/62 /* Трассировка оболочки: главная,
программа, обратите внимание на аргументы */
++ pwd /* Трассировка оболочки: вспомогательные программы */
++ /bin/pwd
1c1 /* Вывод diff */
< /home/arnold/work/prenhall/progex
---
> /d/home/arnold/work/prenhall/progex
Это показано на рис. 9.6.
![](https://cdn.pochitay-knigi.com/s20/3/3/1/8/1/3/img/img_21.jpg)
Рис. 9.6. Замещение процесса
Если на вашей системе есть /dev/fd, вы также можете использовать преимущества этой возможности. Однако, будьте осторожны и задокументируйте то, что вы делаете. Манипуляции с дескриптором файла на уровне С значительно менее прозрачны, чем соответствующие записи оболочки!
9.4.3. Управление атрибутами файла: fcntl()
Системный вызов fcntl() («управление файлом») предоставляет контроль над различными атрибутами либо самого дескриптора файла, либо лежащего в его основе открытого файла. Справочная страница GNU/Linux fcntl(2) описывает это таким способом:
#include <unistd.h> /* POSIX */
#include <fcntl.h>
int fcntl (int fd, int cmd);
int fcntl(int fd, int cmd, long arg);