int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
Другими словами, функция принимает по крайней мере два аргумента; в зависимости от второго аргумента, она может принимать и третий аргумент.
Последняя форма, в которой третий аргумент является указателем на struct flock
, предназначена для блокировки файла. Блокировка файлов сама по себе представляет большую тему; мы отложим обсуждение до раздела 14.2 «Блокировка файлов».
9.4.3.1. Флаг close-on-exec
После вызова fork()
и перед вызовом exec()
следует убедиться, что новая программа наследует лишь те открытые файлы, которые ей нужны. Вы не захотите, чтобы порожденный процесс мешался в открытых файлах родителя, если только это так не задумано. С другой стороны, если у родителя множество открытых файлов, это будет искусственно ограничивать число новых файлов, которые может открыть порожденный процесс. (См. сопроводительную врезку.)
Организационно такое поведение может представлять проблему. Часть вашей программы, порождающая новый процесс, не должна особенно нуждаться в других частях программы, манипулирующей открытыми файлами. И цикл наподобие следующего неприятный, поскольку может не быть открытых файлов:
int j;
for (j = getdtablesize(); j >= 3; j--) /* закрыть все, кроме 0, 1, 2 */
(void)close(j);
Решением является флаг exec
. Установив этот флаг сразу после открытия файла, вам не нужно беспокоиться о том, что какой-нибудь порожденный процесс случайно его унаследует. (Оболочка автоматически устанавливает этот флаг для всех дескрипторов файлов, которые она открывает, начиная с номера 3 и выше.)
Аргумент cmd
имеет два значения, относящиеся к флагу close-on-exec:
F_GETFD
Получает флаги дескриптора файла. Возвращаемое значение является значением всех установленных флагов дескриптора или -1 при ошибке.
F_SETFD
Устанавливает флаги дескриптора файла в содержащееся в arg
(третий аргумент) значение. Возвращаемое значение равно 0 при успехе или -1 при ошибке.
В настоящий момент определен лишь один «флаг дескриптора файла»: FD_CLOEXEC
. Эта именованная константа является нововведением POSIX[100], а большая часть кода использует просто 1 или 0:
if (fcntl(fd, F_SETFD, 1) < 0) ...
/* установить close-on-exec, обработать ошибки */
if (fcntl(fd, F_GETFD) == 1) ...
/* бит close-on-exec уже установлен */
Однако, определение POSIX допускает дальнейшее расширение, поэтому правильный способ написания такого кода больше соответствует этим строкам:
int fd;
long fd_flags;
if ((fd_flags = fcntl(fd, F_GETFD)) < 0) /* Получить флаги */
/* обработать ошибки */
fd_flags |= FD_CLOEXEC; /* Add close-on-exec flag */
if (fcntl(fd, F_SETFD, fd_flags) < 0) /* Установить флаги */
/* обработать ошибки */
ЗАМЕЧАНИЕ. Флаг close-on-exec является собственностью дескриптора, а не лежащего в его основе файла. Поэтому новый дескриптор, возвращенный функциями dup()
или dup2()
(или fcntl()
с F_DUPD
, которую мы намереваемся посмотреть), не наследует установки флага close-on-exec первоначального дескриптора. Если вам нужно установить его также и для нового дескриптора файла, вы должны не забыть сделать это сами. Такое поведение имеет смысл: если вы просто вызвали dup()
, копируя один конец канала в 0 или 1, вы не захотите, чтобы система закрыла его вместо вас, как только процесс осуществит exec!