Неблокирующий ввод/вывод действительно усложняет вашу жизнь, в этом нет никакого сомнения. Но для многих приложений он является необходимостью, позволяющей выполнить задание. Снова рассмотрите спулер печати. Демон спулера не может позволить себе находиться в блокирующем read()
для файла FIFO, которому представлены входящие задания. Он должен иметь также возможность отслеживать запущенные задания и, возможно, периодически проверять состояние печатающих устройств (например, убедиться, что не заело бумагу).
9.4.3.5. Сводка fcntl()
Сводка для системного вызова fcntl()
приведена в табл. 9.5.
Таблица 9.5. Сводка fcntl()
Значение cmd | Значение arg | Возвращает |
---|---|---|
F_DUPFD | Наименьший новый дескриптор | Дублирует аргумент fd |
F_GETFD | Получает флаги дескриптора файла (close-on-exec) | |
F_SETFD | Новое значение флага | Устанавливает флаги дескриптора файла (close-on-exec) |
F_GETFL | Получает флаги основного файла | |
F_SETFL | Новое значение флага | Устанавливает флаги основного файла |
Флаги создания, статуса и прав доступа файла копируются, когда дескриптор файла дублируется. Флаг close-on-exec не копируется.
9.5. Пример: двусторонние каналы в gawk
ksh
) ввела двусторонние каналы на уровне языка, обозначив термином
print -p "команда базы данных" /* Записать в сопроцесс */
read -p db_response /* Прочесть из сопроцесса */
Здесь ksh
. У
Рис. 9.7. Сопроцессы оболочки Корна
В обычном awk
каналы к или от подпроцесса являются односторонними: нет способа послать данные в программу и прочесть посланные от нее в ответ данные — нужно использовать временный файл. GNU awk
(gawk
) заимствует обозначение '|&
' от ksh
для расширения языка awk
:
print "
"
gawk
использует запись '|&
' также для сокетов TCP/IP и порталов BSD, которые не рассматриваются в данной книге. Следующий код из io.c
в дистрибутиве gawk
3.1.3 является частью функции two_way_open()
, которая устанавливает простой сопроцесс: она создает два канала, порождает новый процесс и осуществляет все манипуляции с дескриптором файла. Мы опустили ряд не относящихся к делу частей кода (эта функция занимает больше места, чем следовало бы):
1561 static int
1562 two_way_open(const char *str, struct redirect *rp)
1563 {
...
1827 /* случай 3: двусторонний канал с порожденным процессом */
1828 {
1829 int ptoc[2], сtop[2];
1830 int pid;
1831 int save_errno;
1835
1836 if (pipe(ptoc) < 0)
1837 return FALSE; /* установлен errno, диагностика от вызывающего */
1838
1839 if (pipe(ctop) < 0) {
1840 save_errno = errno;
1841 close(ptoc[0]);
1842 close(ptoc[1]);
1843 errno = save_errno;
1844 return FALSE;
1845 }
Первым шагом является создание двух каналов, ptoc
является каналом «от родителя к потомку», а ctop
— «от потомка к родителю». Во время чтения держите в уме, что индекс 0 является читаемым концом, а 1 — записываемым.