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
int dup(int oldfd);
dup()
возвращает файловый дескриптор, который ссылается на тот же inode, что и oldfd
, или -1
в случае ошибки, oldfd
остается корректным дескриптором, по-прежнему ссылающимся на исходный файл. Новый файловый дескриптор — это всегда наименьший доступный файловый дескриптор. Если процессу нужно получить новый файловый дескриптор с определенным значением (например, 0
, чтобы перенаправить стандартный ввод), то он должен использовать dup2()
.
#include
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. Создание неименованных каналов
Неименованные каналы подобны именованным, но они в файловой системе не существуют. Они не имеют путевых имен, ассоциированных с ними, и все они и их следы исчезают после того, как последний файловый дескриптор, ссылающийся на них, закрывается. Они почти исключительно используются для межпроцессных коммуникаций между дочерними и родительскими процессами либо между родственными процессами.
Оболочки применяют неименованные каналы для выполнения команд вроде ls | head
. Процесс ls
пишет в тот канал, из которого head
читает свой ввод, выдавая ожидаемый пользователем результат.
Создание неименованного канала выполняется по двум файловым дескрипторам, один из которых доступен только для чтения, а второй — только для записи.
#include
int pipe(int fds[2]);
Единственный параметр-массив включает два файловых дескриптора — fd[0]
для чтения и fd[1]
для записи.
11.7. Добавление перенаправления для ladsh