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
) ввела двусторонние каналы на уровне языка, обозначив термином
сопроцесса (coprocess):
<i>команды и аргументы движка базы данных</i> |& /* Запустить сопроцесс в фоновом режиме */
print -p "команда базы данных" /* Записать в сопроцесс */
read -p db_response /* Прочесть из сопроцесса */
Здесь движок базы данных представляет любую серверную программу, которая может управляться интерфейсной частью, в данном случае, сценарием
ksh
. У
движка базы данных стандартный ввод и стандартный вывод подсоединены к оболочке посредством двух отдельных односторонних каналов.
[102] Это показано на рис. 9.7.
Рис. 9.7. Сопроцессы оболочки Корна
В обычном
awk
каналы к или от подпроцесса являются односторонними: нет способа послать данные в программу и прочесть посланные от нее в ответ данные — нужно использовать временный файл. GNU
awk
(
gawk
) заимствует обозначение '
|&
' от
ksh
для расширения языка
awk
:
print "<i>команда</i>" |& "<i>движок базы данных</i>" /* Запустить сопроцесс, записать в него */
"<i>движок базы данных</i>" |& getline db_response /* Прочесть из сопроцесса */
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 — записываемым.
Строки 1836–1837 создают первый канал,
ptoc
. Строки 1839–1845 создают второй канал, закрывая при неудачном создании и первый. Это важно. Небрежность в закрытии открытых, но не используемых каналов ведет к
утечкам дескрипторов файлов. Как и память, дескрипторы файлов являются конечным ресурсом, и когда они иссякают, то теряются.
[103] То же верно и для открытых файлов: убедитесь, что ваш обрабатывающий ошибки код всегда закрывает все открытые файлы и каналы, которые не нужны, когда происходит ошибка.
save_errno
сохраняет значения
errno
, установленные
pipe()
, на тот редкий случай, когда
close()
может завершиться неудачей (строка 1840). Затем
errno
восстанавливается в строке 1843.
1906 if ((pid = fork()) < 0) {
1907 save_errno = errno;
1908 close(ptoc[0]); close(ptoc[1]);
1909 close(ctop[0]); close(ctop[1]);
1910 errno = save_errno;