. Команда определяется содержимым аргумента
.
Некоторые операции, описанные далее, поддерживаются только начиная с
определённой версии ядра Linux. Корректным методом проверки доступности
операции в ядре является вызов fcntl() с желаемой операцией в cmd и
сравнение кода возврата вызов с EINVAL, который указывает на
неподдерживаемость значения ядром.
При успешном выполнении этой команды, возвращается новый файловый
дескриптор.
(2).
Следующие команды работают с флагами, связанными с файловым дескриптором. В
настоящее время определён только один флаг:
, флаг
закрытия-при-выполнении. Если бит
(2), иначе он будет
закрыт.
Каждое описание открытого файла имеет несколько связанных с ним флагов
состояния, которые инициализируются вызовом
(). Эти флаги совместно используются
копиями файловых дескрипторов (сделанными с помощью
(2) и т.д.), которые указывают на одно описание
открытого файла.
В Linux реализована обычная («попроцессная») блокировка UNIX,
стандартизованная POSIX. Описание Linux-альтернативную блокировку открытых
файловых описаний с лучшей семантикой смотрите далее.
POSIX.1-2001 позволяет (но не требует) реализации поддерживать отрицательное
значение l_len; если l_len отрицательно, то интервал, описываемый
lock, имеет размер l_start+l_len до l_start-1 включительно. Это
поддерживается в Linux начиная с ядер версии 2.4.21 и 2.5.49.
Если одна или более несовместимых блокировок мешают установке этой
блокировки, то fcntl() возвращает подробности об одной из этих блокировок
в полях l_type, l_whence, l_start и l_len структуры
lock. Если конфликтующая блокировка является обычной (попроцессной), то в
l_pid записывается значение PID того процесса, который удерживает
блокировку. Если конфликтующая блокировка является блокировку открытого
файлового описания, в l_pid записывается -1. Заметим, что возвращаемая
информация может уже устареть в момент проверки вызывающим.
Записи о блокировках, описанных выше, связаны с процессом (в отличие от
блокировках открытых файловых описаний, описанных далее). Это приводит к
некоторым печальным последствиям:
Нити процесса совместно используют блокировки процесса. Другими словами
многонитевая программа не может использовать блокировку для разграничения
доступа к одной области файла среди нитей.
Блокировки открытых файловых описаний решают обе эти проблемы.
Блокировки открытых файловых описаний (не POSIX)
Блокировки открытых файловых описаний являются консультативными блокировками
диапазона байт, чьё действие почти идентично обычным блокировкам, описанным
выше. Данный тип блокировок есть только в Linux и доступен с версии 3.15
(есть предложение в Austin Group включить данный тип блокировки в следующую
версию POSIX.1). Описание открытых файловых описаний смотрите в open(2).
Принципиальное различие между двумя типами блокировок в том, что обычные
блокировки связаны с процессом, а блокировки открытых файловых описаний
связаны с открытым файловым описанием, для которого они получены (очень
похоже на блокировки, получаемые с помощью flock(2). В следствие этого (и
в отличие от обычных консультативных блокировок), блокировки открытых
файловых описаний наследуются при fork(2) (и при clone(2) с
CLONE_FILES), и освобождаются только автоматически при последнем закрытии
открытого файлового описания, а не при любом закрытии файла.
Конфликт комбинаций блокировок (блокировка чтения и блокировка записи или
две блокировки записи), при котором одна блокировка — открытое файловое
описание, а другая — обычная блокировка, конфликтуют даже когда они
запрашиваются одним процессом для одного и того же файлового дескриптора.
Блокировки открытых файловых описаний, полученные для одного и того же
файлового описания (т. е., для одного и того же файлового дескриптора или
его копии, созданной в результате fork(2), dup(2), fcntl(2)
F_DUPFD и т. п.), всегда совместимы: если новая блокировка помещается на
уже заблокированную область, то существующая блокировка преобразуется в
блокировку нового типа (такие преобразования могут приводить к разделению,
сокращению или объединению существующей блокировки, как описывалось ранее).
С другой стороны, блокировки открытых файловых описаний могут конфликтовать
друг с другом, когда они запрашиваются через разные открытые файловые
описания. То есть, нити в многонитевых программах могут использовать
блокировки открытых файловых описаний для синхронизации доступа к области
файла, если каждая нить выполняет отдельный вызов open(2) на файл и
применяет блокировки для получаемого файлового дескриптора.
Как и у обычных блокировок, третий аргумент fcntl(), lock, является
указателем на структуру flock. Но в отличие от обычных блокировок при
использовании команд, описанных далее, поле l_pid этой структуры должно
иметь значение 0.
Команды для работы с блокировками открытых файловых описаний аналогичны
используемым для обычных блокировок:
F_OFD_SETLK (struct flock *)
Установить блокировку открытого файлового описания (когда l_type равен
F_RDLCK или F_WRLCK) или снять блокировку открытого файлового описания
(когда l_type равен F_UNLCK) байтов, указанных полями l_whence,
l_start и l_len структуры lock. Если конфликтующая блокировка
удерживается другим процессом, то данный вызов вернёт -1 и установит
значение errno в EAGAIN.
F_OFD_SETLKW (struct flock *)
Как F_OFD_SETLK, но если конфликтующая блокировка удерживается на файле,
то выполняется ожидание снятия этой блокировки. Если во время ожидания
поступил сигнал, то данный вызов прерывается и (после возврата из
обработчика сигнала) из него происходит немедленный возврат (возвращается
значение -1 и errno устанавливается в EINTR; см. signal(7)).
F_OFD_GETLK (struct flock *)
В начала данного вызова значение lock описывает блокировку открытого
файлового описания, которую мы бы хотели создать на файле. Если блокировка
возможна, то в действительности fcntl() её не создаёт, а возвращает
F_UNLCK в поле l_type у lock, оставляя остальные поля
неизменными. Если есть одна или более несовместимых блокировок,
препятствующих получению, то в lock возвращается подробная информация об
этих блокировках, как описывалось выше для F_GETLK.
В текущей реализации для блокировок открытых файловых описаний обнаружение
взаимных блокировок не выполняется (в этом отличие от попроцессных
блокировок, для которых ядро выполняет обнаружение взаимных блокировок).
Обязательная (mandatory) блокировка
Предупреждение: реализация обязательного блокирования в Linux
ненадёжна. Смотрите раздел ДЕФЕКТЫ далее.
По умолчанию, обычные (связанные с процессом) блокировки и блокировки
открытого файлового описания являются консультативными. Консультативные
блокировки не обязательны к выполнению и полезны только в сотрудничающих
процессах.
Оба типа блокировки могут быть также обязательными. Если процесс пытается
получить несовместимый доступ (например, read(2) и write(2)) к области
файла, на которую установлена несовместимая обязательная блокировка, то
результат зависит от состояния флага O_NONBLOCK в описании этого
открытого файла. Если флаг O_NONBLOCK не установлен, то системный вызов
блокируется до удаления блокировки или преобразуется в режим, который
совместим с доступом. Если флаг O_NONBLOCK установлен, то системный вызов
завершается с ошибкой EAGAIN.
Чтобы использовать обязательные блокировки, обязательное блокирование должно
быть включено в файловой системе, содержащей файл, и на самом
файле. Обязательное блокирование включается в файловой системе с помощью
параметра «-o mand» команды mount(8) или с помощью флага MS_MANDLOCK в
mount(2). Обязательное блокирование включается на файле посредством
отключения права исполнения группе и установкой бита set-group-ID
(см. chmod(1) и chmod(2)).
Обязательная блокировка не описана в POSIX. В некоторых других системах
обязательная блокировка также поддерживается, хотя процесс её создания
различен.
Управление сигналами
Для управления сигналами доступности ввода/вывода используются команды
F_GETOWN, F_SETOWN, F_GETOWN_EX, F_SETOWN_EX, F_GETSIG и
F_SETSIG:
F_GETOWN (void)
Получить (как результат работы функции) идентификатор процесса или группы
процесса, который в текущий момент принимает сигналы SIGIO и SIGURG
для событий на файловом дескрипторе fd. Идентификатор процесса
возвращается как положительное число; идентификатор группы возвращается как
отрицательное число (но см. раздел ДЕФЕКТЫ далее). Аргумент arg
игнорируется.
F_SETOWN (int)
Установить идентификатор процесса или группы процесса, которые будут
принимать сигналы SIGIO и SIGURG для событий на файловом дескрипторе
fd; идентификатор задаётся в аргументе arg. Идентификатор процесса
задаётся положительным числом, идентификатор группы задаётся отрицательным
числом. Обычно, вызывающий процесс указывает самого себя в качестве
принимающего (то есть в arg указывается результат getpid(2)).
Если вы установили на файловый дескриптор флаг состояния O_ASYNC с
помощью команды F_SETFL в fcntl(), то сигнал SIGIO посылается
всякий раз, когда для данного файлового дескриптора становится возможным
ввод или вывод. F_SETSIG можно использовать для включения доставки
сигнала, отличного от SIGIO. Если такая проверка разрешения завершится
неудачно, то сигнал просто отбрасывается.
Отправка сигнала процессу-владельцу (группе), указанному с помощью
F_SETOWN --- такая же проверка прав, как описанная для kill(2), где
посылающий процесс один из тех, который может пользоваться F_SETOWN (но
см. раздел ДЕФЕКТЫ далее).
Если файловый дескриптор fd указывает на сокет, то по команде F_SETOWN
для него также выбирается получатель сигналов SIGURG, которые
доставляются, когда на сокет поступают внеполосные данные. (SIGURG
посылается во всех ситуациях, когда вызов select(2) говорит, что сокет
находится в состоянии "исключительной ситуации".)
Следующее верно для ядер 2.6.x, до 2.6.11 включительно:
Если для F_SETSIG передаётся ненулевое значение в многонитивой процесс,
работающий с библиотекой нитей (например, NPTL), которая обеспечивает
поддержку групп нитей, то положительное значение, переданное F_SETOWN,
имеет другой смысл: вместо указания ID процесса, описывающего весь процесс,
она является ID нити, указывающим на определённую нить процесса. Поэтому
может понадобиться передать в F_SETOWN результат gettid(2), а не
getpid(2), чтобы получить правильный результат при использовании
F_SETSIG. (В имеющихся реализациях Linux ID главной нити совпадает с ID
процесса. Это означает, что в программе с одной нитью можно использовать
любой вызов, gettid(2) или getpid(2), в этом случае.) Однако заметим,
что утверждения этого абзаца не применимы к сигналу SIGURG, генерируемому
для внеполосных данных сокета: этот сигнал всегда посылается или процессу
или группе процессов, в зависимости от значения, указанного для F_SETOWN.
Описанное выше поведение было случайно удалено из Linux 2.6.12, и так и не
восстановлено. Начиная с Linux 2.6.32 используйте F_SETOWN_EX при
назначении сигналов SIGIO и SIGURG для определённой нити.
F_GETOWN_EX (struct f_owner_ex *) (начиная с Linux 2.6.32)
Получить настройки владения текущим файловым дескриптором, установленные
предыдущей командой F_SETOWN_EX. Информация возвращается в структуре,
указанной в arg, которая имеет следующий вид:
struct f_owner_ex {
int type;
pid_t pid;
};
Поле type будет равно: F_OWNER_TID, F_OWNER_PID или
F_OWNER_PGRP. Значением поля pid будет положительное целое,
представляющее ID нити, ID процесса или ID группы процессов. Подробности
смотрите в описании F_SETOWN_EX.
F_SETOWN_EX (struct f_owner_ex *) (начиная с Linux 2.6.32)
Эта команда выполняет задачу, подобную F_SETOWN. Она позволяет
вызывающему назначить сигналы доступности ввода-вывода определённой нити,
процессу или группе процессов. Вызывающий указывает приёмник сигналов в
arg, выражаемый указателем на структуру f_owner_ex. Поле type имеет
одно из следующих значений, которое определяет чем считать pid:
F_OWNER_TID
Посылать сигнал нити, чей ID (значение, возвращаемое вызовом clone(2) или
gettid(2)) указан в pid.
F_OWNER_PID
Посылать сигнал процессу, чей ID указан в pid.
F_OWNER_PGRP
Посылать сигнал группе процессов, чей ID указан в pid. (Заметим, что в
отличие от F_SETOWN, ID группы процессов здесь задаётся как положительное
значение.)
F_GETSIG (void)
Получить (как результат функции) сигнал, посылаемый, когда становится
возможным ввод или вывод. Значение 0 означает сигнал SIGIO. Любое другое
значение (включая SIGIO) является другим сигналом, и в этом случае для
обработчика сигнала доступна дополнительная информация, если он был
установлен с SA_SIGINFO. Аргумент arg игнорируется.
F_SETSIG (int)
Установить сигнал, который будет посылаться когда станет возможен ввод или
вывод, в значение, указанное в arg. Значение 0 означает сигнал по
умолчанию SIGIO. Любое другое значение (включая SIGIO) является другим
сигналом, и в этом случае, для обработчика сигнала доступна дополнительная
информация, если он был установлен с SA_SIGINFO.
В случае использования F_SETSIG с ненулевым значением и установкой
SA_SIGINFO для обработчика сигнала (см. sigaction(2)) обработчику
передаётся дополнительная информация о событиях ввода/вывода в структуре
siginfo_t. Если поле si_code показывает, что источник ---
SI_SIGIO, то поле si_fd содержит файловый дескриптор, ассоциированный
с событием. В противном случае не существует никакого механизма, чтобы
сообщить с каким файловым дескриптором связан полученный сигнал, и вы должны
использовать обычные механизмы (select(2), poll(2), read(2) с
установленным O_NONBLOCK и т.д.), чтобы определить какой файловый
дескриптор доступен для ввода/вывода.
Заметим, что файловый дескриптор, предоставляемый в si_fd, тот же, что
указывался при операции F_SETSIG. Это может привести к редкой тупиковой
ситуации. Если с файлового дескриптора делался дубль (dup(2) или подобным
вызовом), и оригинальный файловый дескриптор закрыт, то события ввода-вывода
будут продолжать генерироваться, но поле si_fd будет содержать номер
теперь уже закрытого файлового дескриптора.
При выборе сигнала реального времени (значение >= SIGRTMIN) в очередь
может добавляться несколько событий ввода-вывода с одинаковыми номерами
сигналов (размер очереди зависит от доступной памяти). Дополнительная
информация будет доступна как описано выше, если для обработчика сигнала
будет установлено SA_SIGINFO.
Заметим, что в Linux есть предел на количество сигналов реального времени,
которые могут находиться в очереди процесса (см. getrlimit(2) и
signal(7)), и если этот предел достигнут, то ядро изменяет пункт доставки
SIGIO, и этот сигнал доставляется всему процессу, а не указанной нити.
Используя эти механизмы, программа может реализовать полностью асинхронный
ввод-вывод почти не используя в своей работе select(2) или poll(2).
Использование O_ASYNC, является специфичным для BSD и Linux. В POSIX.1
описано только использование F_GETOWN и F_SETOWN вместе с сигналом
SIGURG для сокетов (в POSIX не определён сигнал
SIGIO). F_GETOWN_EX, F_SETOWN_EX, F_GETSIG, and F_SETSIG есть
только в Linux. В POSIX описан асинхронный ввод-вывод и структура
aio_sigevent, используемая для сходных действий; они также доступны в
Linux как часть библиотеки GNU C (Glibc).
Аренда
Команды F_SETLEASE и F_GETLEASE (в Linux 2.4 и выше) используются,
соответственно, для установки новой и получения текущей аренды открытого
описания файла, на который указывает файловый дескриптор fd. Аренда файла
предоставляет механизм, посредством которого процесс, который удерживает
аренду («арендатор»), уведомляется (отправкой сигнала), когда процесс
(«нарушитель аренды») пытается выполнить вызов open(2) или truncate(2)
на файл, указанный в этом файловом дескрипторе.
F_SETLEASE (int)
Установить или удалить аренду файла, в соответствии со значениями,
указываемыми в arg:
F_RDLCK
Установить аренду чтения. Это приведёт к генерации уведомления вызывающего
процесса, когда файл открывается для записи или усечения. Аренда чтения
может быть выделена только на файловый дескриптор, открытый только на
чтение.
F_WRLCK
Установить аренду записи. Это приведёт к генерации уведомления вызывающего
процесса, когда файл открывается для чтения или записи или выполняется его
усечение. Аренда записи может быть установлена на файл, только если этот
файл не имеет других открытых файловых дескрипторов.
F_UNLCK
Удалить аренду с указанного файла.
Аренды ассоциируются с открытым файловым описанием (см. open(2)). Это
значит, что дублированные файловые дескрипторы (созданные, например,
fork(2) или dup(2)) указывают на одну и ту же аренду, и эта аренда
может изменяться или освобождаться через любой из этих дескрипторов. Более
того, аренда освобождается или через явную команду F_UNLCK на любом из
этих дублированных файловых дескрипторов, или когда все эти файловые
дескрипторы будут закрыты.
Аренды могут быть выданы только на обычные файлы. Непривилегированный
процесс может получить аренду только на файл, чей UID (владельца) совпадает
с UID на файловой системе процесса. Процесс с мандатом CAP_LEASE может
получить аренду на любые файлы.
F_GETLEASE (void)
Узнать какой тип аренды ассоциирован с файловым дескриптором fd;
возвращается одно из значений F_RDLCK, F_WRLCK или F_UNLCK,
соответственно означающих аренду на чтение, запись или что аренды
нет. Аргумент arg игнорируется.
Когда процесс («нарушителя аренды») выполняет вызов open(2) или
truncate(2), который конфликтует с арендой, установленной через
F_SETLEASE, то системный вызов блокируется ядром и ядро уведомляет
арендатора сигналом (по умолчанию SIGIO). Арендатор должен при получении
этого сигнала выполнить все необходимые действия по очистке для подготовки
этого файла к использованию другим процессом (например, сбросить буферы
кэша) и затем удалить или снизить условия аренды. Аренда удаляется по
команде F_SETLEASE с аргументом arg, установленным в F_UNLCK. Если
арендатор удерживает аренду на запись в файл, и нарушитель аренды открывает
файл на чтение, то достаточно того, что арендатор понизит условия аренды до
аренды на чтение. Это выполняется командой F_SETLEASE с аргументом
arg, установленным в F_RDLCK.
Если арендатор не освободит аренду или не снизит условия в течении
определённого количества секунд, указанного в файле
/proc/sys/fs/lease-break-time, то ядро принудительно удалит или снизит
условия аренды для арендатора.
После того, как был начат разрыв аренды, F_GETLEASE возвращает тип
назначения аренды (или F_RDLCK или F_UNLCK, в зависимости от
необходимости совместимости с нарушителем аренды) до тех пор, пока держатель
аренды добровольно не отдаст или не удалит аренду или ядро принудительно не
сделает это после истечения таймера разрыва аренды.
После того как аренда снята держателем аренды или принудительно удалена и
снижены условия, и предполагая, что нарушитель аренды не выполнял
неблокирующий системный вызов, ядро позволяет продолжить работу системного
вызова нарушителя аренды.
Если нарушитель аренды, заблокированный в open(2) или truncate(2),
прерывается обработчиком сигнала, то системный вызов завершается неудачно с
ошибкой EINTR, но другие шаги по-прежнему выполняются как описано
ранее. Если нарушитель аренды завершается по сигналу будучи блокированным в
open(2) или truncate(2), то другие шаги по-прежнему выполняются как
описано ранее. Если нарушитель аренды указал флаг O_NONBLOCK при вызове
open(2), то вызов немедленно завершается неудачей с ошибкой
EWOULDBLOCK, но другие шаги по-прежнему выполняются как описано ранее.
По умолчанию, для уведомления арендатора используется сигнал SIGIO, но
его можно изменить, используя команду F_SETSIG для fcntl(). Если
выполняется команда F_SETSIG (даже назначая сигнал SIGIO), и при этом
обработчик сигнала устанавливается с использованием SA_SIGINFO, то
обработчик получит в качестве второго аргумента структуру siginfo_t, в
которой поле si_fd будет содержать файловый дескриптор арендованного
файла, к которому пытается получить доступ другой процесс (это полезно, если
вызывающий процесс удерживает аренду на несколько файлов).
Уведомления об изменении файла и каталога (dnotify)
F_NOTIFY (int)
(Начиная с Linux 2.4) Уведомлять при смене каталога, на который указывает
fd или когда изменились файлы, которые в нём содержатся. События, о
наступлении которых делается уведомление, задаются в аргументе arg,
который является битовой маской, получаемой сложением (OR) одного или более
следующих бит:
DN_ACCESS
Был произведён доступ к файлу (read(2), pread(2), readv(2) и
подобные).
DN_MODIFY
Файл был изменён (write(2), pwrite(2), writev(2), truncate(2),
ftruncate(2) и подобные).
DN_CREATE
Файл был создан (open(2), creat(2), mknod(2), mkdir(2),
link(2), symlink(2), rename(2) в этом каталоге).
DN_DELETE
Файл был удалён (unlink(2), rename(2) в другой каталог, rmdir(2)).
DN_RENAME
Файл был переименован внутри каталога (rename(2)).
DN_ATTRIB
У файла были изменены атрибуты (chown(2), chmod(2), utime(2),
utimensat(2) и подобные).
(Чтобы получить эти определения, нужно задать макрос тестирования свойств
_GNU_SOURCE перед всеми остальными заголовочными файлами.)
Уведомления об изменении состояния каталога обычно однократные и приложение
должно перерегистрировать установку уведомлений, чтобы и дальше получать
их. Однако, если в аргумент arg, добавить DN_MULTISHOT, то уведомления
будут приходить до тех пор, пока не будут явно отменены.
Серии запросов F_NOTIFY добавляются к событиям в arg, которые уже
установлены. Чтобы выключить уведомления всех событий, выполните вызов
F_NOTIFY, указав 0 в arg.
Уведомление происходит посредством доставки сигнала. Сигналом по умолчанию
является SIGIO, но это можно изменить с помощью команды F_SETSIG
fcntl() (заметим, что SIGIO — один из безочерёдных стандартных
сигналов; переход к использованию сигнала реального времени означает, что
многократные уведомления могут попасть в очередь процесса). В последнем
случае, обработчик сигнала принимает структуру siginfo_t в качестве
второго аргумента (если обработчик был установлен с помощью SA_SIGINFO),
в поле si_fd этой структуры содержится файловый описатель, для которого
сгенерировано уведомление (полезно при учёте уведомлений из нескольких
каталогов).
Кроме того, когда используется DN_MULTISHOT, для уведомлений должен бы
быть использован сигнал реального времени, так что множественные уведомления
могут быть поставлены в очередь.
ЗАМЕЧАНИЕ: В новых приложениях нужно использовать интерфейс inotify
(доступен начиная с ядра 2.6.13), который предоставляет намного лучший
интерфейс для получения уведомлений о событиях в файловой системе. Смотрите
inotify(7).
Изменение ёмкости канала
F_SETPIPE_SZ (int; начиная с Linux 2.6.35)
Изменяет ёмкость канала, на который указывает fd; она становится равной
не менее arg байт Непривилегированный процесс может подстроить ёмкость
канала до любого значения начиная с размера системной страницы до предела,
заданного в /proc/sys/fs/pipe-max-size (см. proc(5)). При задании
ёмкости меньше размера страницы, она будет без ошибок округлена до размера
страницы. При задании непривилегированным процессом ёмкости канала больше
предела из /proc/sys/fs/pipe-max-size приведёт к ошибке EPERM;
привилегированный процесс (с CAP_SYS_RESOURCE) может превысить этот
ограничение. При выделении буфера под канал ядро может использовать ёмкость
больше чем указано в arg, если это удобно в реализации. Реальным размером
считается возвращаемый результатом функции. Попытка установить ёмкость
канала меньше чем количество пространства в буфере, в настоящее время
используемого для хранения данных, приведёт к ошибке EBUSY.
F_GETPIPE_SZ (void; начиная с Linux 2.6.35)
Возвращает (как результат функции) ёмкость канала, указываемого fd.
Опечатывание файла (file sealing)
Опечатывание файла позволяет ограничить набор выполняемых над файлом
операций. Любая операция с файлом, попавшая в набор опечатанных, завершается
с ошибкой EPERM. Набор печатей по умолчанию зависит от типа файла, к
которому он применяется и файловой системы. Описание опечатывания файла,
предназначение и примеры кода смотрите в memfd_create(2).
В настоящее время опечатывание поддерживается только в файловой системе
tmpfs. В других файловых системах все операции fcntl(2), относящиеся к
печатям, возвращают EINVAL.
Печати (seals) — свойство inode. То есть все открытые файловые дескрипторы,
указывающие на один inode, имеют общий набор печатей. Кроме этого, печати
нельзя удалять, можно только добавлять.
F_ADD_SEALS (int; начиная с Linux 3.17)
Добавляет печати из значения битовой маски arg в набор печатей inode, на
которую ссылается файловый дескриптор fd. Печати из набора нельзя
удалить. После успешного выполнения, печати сразу же учитываются ядром. Если
в текущий набор печатей входит F_SEAL_SEAL (смотрите далее), то этот
вызов завершается с ошибкой EPERM. Добавление уже установленной печати ни
к чему не приводит, если ещё не указана F_SEAL_SEAL. Чтобы разместить
печать файловый дескриптор fd должен быть доступен на запись.
F_GET_SEALS (void; начиная с Linux 3.17)
Возвращает (в виде результата функции) текущий набор печатей inode, на
которую указывает fd. Если печатей нет, возвращается 0. Если файл не
поддерживает опечатывание, то возвращается -1 и errno присваивается
EINVAL.
Доступны следующие печати:
F_SEAL_SEAL
Если печать установлена, то последующие вызовы fcntl(2) с F_ADD_SEALS
будут завершаться EPERM. Таки образом, данная печать предотвращает
изменения самого набора печатей. Если начальный набор печатей файла содержит
F_SEAL_SEAL, то он является постоянным и неизменяемым.
F_SEAL_SHRINK
Если эта печать установлена, то нельзя уменьшить размер файла. Она влияет на
open(2) с флагом O_TRUNC, а также на truncate(2) и
ftruncate(2). Эти вызовы будут завершаться с ошибкой EPERM, если
попытаются уменьшить файл. Увеличение размера файла по-прежнему возможно.
F_SEAL_GROW
Если эта печать установлена, то нельзя увеличить размер файла. Она влияет на
write(2) за концом файла, truncate(2), ftruncate(2) и
fallocate(2). Эти вызовы будут завершаться с ошибкой EPERM, если
попытаются увеличить файл. Уменьшение размера файла по-прежнему возможно.
F_SEAL_WRITE
Если эта печать установлена, то нельзя изменить содержимое файла. Заметим,
что уменьшение или увеличение файла по-прежнему возможно. То есть данная
печать, обычно, используется в комбинации с одной из этих печатей. Данная
печать влияет на write(2) и fallocate(2) (только для флага
FALLOC_FL_PUNCH_HOLE). Эти вызовы будут завершаться с ошибкой
EPERM. Кроме этого, попытка создать новое общее, доступное на запись
отображение через mmap(2) также завершится с ошибкой EPERM.
Установка F_SEAL_WRITE через fcntl(2) с печатью F_ADD_SEALS
завершится EBUSY, если существует доступное на запись общее
отображение. Такие отображения должны быть удалены перед добавлением данной
печати. Кроме этого, если у файла есть асинхронные операции, ожидающие
ввода-вывода (io_submit(2)), то все отложенные записи будут отброшены.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении возвращаемое значение зависит от используемой
команды:
F_DUPFD
Новый файловый дескриптор.
F_GETFD
Значение флагов файлового дескриптора.
F_GETFL
Значение флагов состояния файла.
F_GETLEASE
Тип аренды, установленной на файлом дескрипторе.
F_GETOWN
Значение, представляющее собой владельца файлового дескриптора.
F_GETSIG
Значение сигнала, посылаемого когда становится возможным чтение или запись
или ноль для традиционного поведения SIGIO.
F_GETPIPE_SZ, F_SETPIPE_SZ
Ёмкость канала.
F_GET_SEALS
Битовая маска, описывающая печати inode, на которую указывает fd.
Все остальные команды
Ноль.
В случае ошибки возвращается -1 и значение errno устанавливается
соответствующим образом.
ОШИБКИ
EACCES или EAGAIN
Операция запрещена блокировками, которые удерживаются другими процессами.
EAGAIN
Операция запрещена, потому что файл отображается в память другим процессом.
EBADF
Значение fd не является открытым файловым дескриптором.
EBADF
Значение cmd равно F_SETLK или F_SETLKW, но режим открытия
файлового дескриптора не совпадает с типом запрошенной блокировки.
EBUSY
Значение cmd равно F_SETPIPE_SZ и новая ёмкость канала, указанная в
arg, меньше размера буферного пространства, используемого в данный момент
для хранения данных в канале.
EBUSY
Значение cmd равно F_ADD_SEALS, в arg содержится F_SEAL_WRITE и
существует доступное на запись, общее отображение файла, на который
указывает fd.
EDEADLK
Было обнаружено, что указанная команда F_SETLKW привела бы к взаимной
блокировке (deadlock).
EFAULT
Значение lock находится за пределами доступного адресного пространства.
EINTR
Значение cmd равно F_SETLKW или F_OFD_SETLKW и операция была
прервана сигналом; смотрите signal(7)).
EINTR
Значение cmd равно F_GETLK, F_SETLK, F_OFD_GETLK или
F_OFD_SETLK и операция была прервана сигналом перед тем как блокировка
была проверена или установлена. Большинство таких ошибок случается при
блокировке удалённого файла (например, блокировка через NFS), но иногда
такое может случаться и с локальным файлом.
EINVAL
Значение cmd не распознано ядром.
EINVAL
Значение cmd равно F_ADD_SEALS и arg содержит бит не распознанной
печати.
EINVAL
Значение cmd равно F_ADD_SEALS или F_GET_SEALS и файловая система,
содержащая inode, на которую указывает fd, не поддерживает опечатывание.
EINVAL
Значение cmd равно F_DUPFD и значение arg отрицательное или больше
максимально возможного значения (смотрите описание RLIMIT_NOFILE в
getrlimit(2)).
EINVAL
Значение cmd равно F_SETSIG и значение arg не содержит допустимый
номер сигнала.
EINVAL
Значение cmd равно F_OFD_SETLK, F_OFD_SETLKW или F_OFD_GETLK, но
значение l_pid не равно нулю.
EMFILE
Значение cmd равно F_DUPFD и было достигнуто ограничение по количеству
открытых файловых дескрипторов на процесс.
ENOLCK
Открыто слишком много блокировок сегментов, таблица блокировок заполнена или
ошибка протокола удалённой блокировки (например, при блокировке через NFS).
ENOTDIR
Значение cmd равно F_NOTIFY, fd не ссылается на каталог.
EPERM
Попытка сбросить флаг O_APPEND на файле, который открыт с атрибутом
только для добавления.
EPERM
Значение cmd равно F_ADD_SEALS, но fd не открыт на запись или
текущий набор печатей файла уже содержит F_SEAL_SEAL.
СООТВЕТСТВИЕ СТАНДАРТАМ
SVr4, 4.3BSD, POSIX.1-2001. В POSIX.1-2001 указаны только команды
F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_GETLK,
F_SETLK и F_SETLKW.
Значения F_GETOWN и F_SETOWN определены в POSIX.1-2001. Для получения
их определений, определите _BSD_SOURCE или _XOPEN_SOURCE со значением
500 или больше, или определите _POSIX_C_SOURCE со значением 200809L или
больше.
F_DUPFD_CLOEXEC указана в POSIX.1-2008. Для получения определения,
определите _POSIX_C_SOURCE со значением 200809L или больше, или
_XOPEN_SOURCE со значением 700 или больше.
Команды F_GETOWN_EX, F_SETOWN_EX, F_SETPIPE_SZ, F_GETPIPE_SZ,
F_GETSIG, F_SETSIG, F_NOTIFY, F_GETLEASE и F_SETLEASE есть
только в Linux. Для задействования этих определений определите макрос
_GNU_SOURCE.
Значения F_OFD_SETLK, F_OFD_SETLKW и F_OFD_GETLK есть только в
Linux (и для получения их определений нужно определить _GNU_SOURCE), но
ведётся работа по их включению в следующую версию POSIX.1.
Значения F_ADD_SEALS и F_GET_SEALS есть только в Linux.
ЗАМЕЧАНИЯ
Ошибки, возвращаемые dup2(2), отличаются от тех, что возвращаются при
F_DUPFD.
Файловая блокировка
Первоначальная версия системного вызова fcntl() в Linux не умела работать
с большими файловыми смещениями (в структуре flock). Позднее, в Linux 2.4
был добавлен системный вызов fcntl64(). Новый системный вызов использует
другую структуру для блокировки файлов — flock64 и соответствующие
команды — F_GETLK64, F_SETLK64 и F_SETLKW64. Однако, это различие
может игнорироваться приложениями, которые используют glibc, так как
имеющаяся в ней обёрточная функция fcntl() самостоятельно задействует
более новый системный вызов, если он доступен.
Ошибки, возвращаемые dup2(2), отличаются от тех, что возвращаются при
F_DUPFD.
Обычные блокировки (Record locks)
Начиная с ядра 2.0, не существует разницы между типами блокировки, которые
осуществляют flock(2) и fcntl().
Некоторые системы имеют дополнительные поля в структуре struct flock,
например, l_sysid. Вообще-то, один l_pid не очень полезен, если
процесс, удерживающий блокировку, может работать на другой машине.
Первоначальная версия системного вызова fcntl() в Linux не умела работать
с большими файловыми смещениями (в структуре flock). Позднее, в Linux 2.4
был добавлен системный вызов fcntl64(). Новый системный вызов использует
другую структуру для блокировки файлов — flock64 и соответствующие
команды — F_GETLK64, F_SETLK64 и F_SETLKW64. Однако, это различие
может игнорироваться приложениями, которые используют glibc, так как
имеющаяся в ней обёрточная функция fcntl() самостоятельно задействует
более новый системный вызов, если он доступен.
Блокировка и NFS
До Linux 3.12, если клиент NFSv4 теряет связь с сервером на некоторый период
времени (более 90 секунд), то он может потерять и перезапросить блокировки
даже не зная об этом (период времени, после которого контакт предполагается
потерянным в NFSv4 называется время аренды (leasetime). У Linux в сервере
NFS его можно узнать по значению в /proc/fs/nfsd/nfsv4leasetime, которое
отражает период в секундах. В этом файле значение по умолчанию равно
90). Данный сценарий несёт потенциальный риск повреждения данных, так как в
этот перерыв другой процесс может установить блокировку и выполнить файловый
ввод-вывод.
Начиная с Linux 3.12, если клиент NFSv4 теряет контакт с сервером, то любой
файловый ввод-вывод, выполняемый процессом, который «думает», что имеет
блокировку, будет завершаться с ошибкой до тех пор, пока этот процесс не
закроет и не переоткроет файл. Чтобы вернуть поведение, которое было до
версии pre-3.12, можно параметру ядра nfs.recover_lost_locks присвоить
значение 1, из-за чего клиент буде пытаться восстановить потерянные
блокировки при переустановлении связи с сервером. Из-за наличия
сопутствующего риска повреждения данных, значение данного параметра по
умолчанию равно 0 (отключено).
ДЕФЕКТЫ
F_SETFL
Невозможно использовать F_SETFL для смены состояния флагов O_DSYNC и
O_SYNC. Попытка изменить состояние этих флагов просто игнорируется.
F_GETOWN
Ограничение в соглашениях по системным вызовам Linux на некоторых
архитектурах (в частности i386) приводит к тому, что если значение ID группы
процесса (отрицательное), возвращаемое по команде F_GETOWN, попадает в
диапазон от -1 до -4095, то оно неправильно интерпретируется glibc и
считается ошибкой в системном вызове; то есть возвращаемое значение
fcntl() будет равно -1, а errno будет содержать значение ID группы
процесса (положительное). Команда F_GETOWN_EX (есть только в Linux) не
подвержена этой проблеме. Начиная с glibc версии 2.11, glibc делает проблему
для F_GETOWN невидимой, реализовав F_GETOWN с помощью F_GETOWN_EX.
F_SETOWN
В Linux 2.4 и более раннем, есть ошибка, которая может произойти когда
непривилегированный процесс использует F_SETOWN для задания владельца
дескриптора файла сокета как процесса (группу) отличного от вызывающего. В
этом случае fcntl() может вернуть -1 с errno равным EPERM, даже
когда процесс (группа) владелец такая же как и вызывающий имеет право
посылать сигнал. Несмотря на возвращаемую ошибку, владелец файлового
дескриптора всё равно устанавливается и сигналы будут посылаться владельцу.
Обнаружение взаимных блокировок (deadlock)
Алгоритм обнаружения взаимных блокировок задействуется ядром при работе с
запросами F_SETLKW и может закончиться как ненахождением (не удалось
обнаружить взаимную блокировку и процессы блокируют друг друга навсегда),
так и ошибочным нахождением (ошибка EDEADLK, но взаимная блокировка
отсутствует). Например, ограничение ядра на вложенность зависимостей
блокировки при поиске равна 10, что означает, что цепочки циклических
зависимостей, превышающие этот размер, не будут обнаружены. Также ядро может
ошибочно посчитать за взаимную блокировку ситуацию, когда два и более
процесса, созданных с помощью clone(2) с флагом CLONE_FILES, размещают
блокировки, которые похожи (для ядра) на конфликтующие.
Обязательная (mandatory) блокировка
Реализация обязательной блокировки в Linux проводит к состязательности
процессов, что делает её ненадёжной: вызов write(2), пересекающийся с
блокировкой, может изменить данные после установления обязательной
блокировки; вызов read(2), пересекающийся с блокировкой, может обнаружить
изменившиеся данные, которые были внесены уже установления блокировки на
запись. Подобная состязательность существует между обязательными
блокировками и mmap(2). Поэтому нецелесообразно полагаться на
обязательную блокировку.