int fcntl (int fd, int command, long arg);
Для многих команд arg
не используется. Ниже мы обсудим большую часть применений fcntl()
. Этот вызов используется для блокировки файлов, аренды файлов, неблокирующего ввода-вывода, который рассматривается в главе 13, а также уведомления об изменениях каталогов, представленного в главе 14.
11.5.1. Изменение режима доступа к открытому файлу
Режим добавления (указываемый флагом O_APPEND
при открытии файла) и неблокирующий режим (флаг O_NONBLOCK
), могут быть включены и отключены уже после того, как файл был открыт, с помощью команды F_SETFL
в fcntl()
. Параметр arg
при этом должен содержать флаги, которые нужно установить — если какой-то из флагов не указан для fd
, он отключается.
F_GETFL
можно использовать для запроса текущих установленных флагов файла. Это возвращает все флаги, включая режим чтения/записи для открытого файла. F_SETFL
позволяет только устанавливать упомянутые выше флаги — любые другие флаги, представленные в аргументе arg
, игнорируются.
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_RDONLY);
Такой вызов абсолютно правильный, но он не делает ничего. Включение режима добавления для открытого файлового дескриптора выглядит так, как показано ниже.
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND);
Следует отметить, что это предохраняет установку O_NONBLOCK
. Отключение режима добавления выглядит похоже.
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_APPEND);
11.5.2. Модификация флага 'закрыть при выполнении'
Во время системного вызова exec()
дескрипторы файлов обычно остаются открытыми для использования в новых программах. В некоторых случаях может потребоваться, чтобы файлы закрывались, когда вызывается exec()
. Вместо закрытия их вручную вы можете попросить систему закрыть соответствующий файловый дескриптор при вызове exec()
с помощью команд F_GETFD
и F_SETFD
в fcntl()
. Если флаг 'закрыть при выполнении' (close-on-exec) установлен, когда применяется F_GETFD
, возвращается ненулевое значение, в противном случае возвращается ноль. Флаг 'закрыть при выполнении' устанавливается командой F_SETFD
; он отключается, если arg
равно 0, в противном случае он включается.
Ниже показано, как можно заставить fd
закрываться, когда процесс вызывает exec()
.
fcntl(fd, F_SETFD, 1);
11.5.3. Дублирование файловых дескрипторов
Иногда процессам требуется создать новый файловый дескриптор, который ссылается на ранее открытый файл. Командные оболочки используют эту функциональность для перенаправления стандартного ввода, вывода и потока ошибок по запросу пользователя. Если процессу не важно, какой файловый дескриптор будет использован для новой ссылки, он должен использовать dup()
.
#include <unistd.h>
int dup(int oldfd);
dup()
возвращает файловый дескриптор, который ссылается на тот же inode, что и oldfd
, или -1
в случае ошибки, oldfd
остается корректным дескриптором, по-прежнему ссылающимся на исходный файл. Новый файловый дескриптор — это всегда наименьший доступный файловый дескриптор. Если процессу нужно получить новый файловый дескриптор с определенным значением (например, 0
, чтобы перенаправить стандартный ввод), то он должен использовать dup2()
.
#include <unistd.h>
int dup2(int oldfd, int newfd);
Если newfd
ссылается на уже открытый дескриптор, то он закрывается. Если вызов завершен успешно, он возвращает новый файловый дескриптор и newfd
ссылается на тот же файл, что oldfd
. Системный вызов fcntl()
представляет почти ту же функциональность командой F_DUPFD
. Первый аргумент — fd
— это уже открытый файловый дескриптор. Новый файловый дескриптор — это первый доступный дескриптор, равный или больший, чем значение последнего аргумента fcntl()
. (В этом состоит отличие от работы dup2()
.) Вы можете реализовать dup2()
через fcntl()
следующим образом.
int dup2(int oldfd, int newfd) {
close(newfd); /* ensure new fd is available */
return fcntl(oldfd, F_DUPFD, newfd);
}
Создание двух файловых дескрипторов, ссылающихся на один и тот же файл — это не то же самое, что открытие файла дважды. Почти все атрибуты дублированных дескрипторов разделяются: они разделяют текущую позицию, режим доступа и блокировки. (Все это записывается в файловой структуре[50], которая создается при каждом открытии файла. Файловый дескриптор ссылается на файловую структуру, и дескриптор, возвращенный dup()
, ссылается на одну и ту же структуру.) Единственный атрибут, который может независимо управляться в этих двух дескрипторах — это состояние 'закрыть при выполнении'. После того, как процесс вызывает fork()
, то файлы, открытые в родительском процессе, наследуются дочерним, и эти пары файловых дескрипторов (один в родительском процессе, другой — в дочернем) ведут себя так, будто файловые дескрипторы были дублированы с текущей позицией и другими разделенными атрибутами[51].
11.6. Создание неименованных каналов
Неименованные каналы подобны именованным, но они в файловой системе не существуют. Они не имеют путевых имен, ассоциированных с ними, и все они и их следы исчезают после того, как последний файловый дескриптор, ссылающийся на них, закрывается. Они почти исключительно используются для межпроцессных коммуникаций между дочерними и родительскими процессами либо между родственными процессами.