Возможность использования вызова fcntl() для изменения флагов состояния открытого файла может особенно пригодиться в следующих случаях.
• Файл был открыт не вызывающей программой, поэтому она не может управлять флагами, использованными в вызове open() (например, файл мог быть представлен одним из стандартных дескрипторов, открытых еще до запуска программы).
• Файловый дескриптор был получен не из open(), а из другого системного вызова. Примерами таких системных вызовов могут служить pipe(), который создает конвейер и возвращает два файловых дескриптора, ссылающихся на оба конца конвейера, и socket(), который создает сокет и возвращает дескриптор файла, ссылающийся на сокет.
Чтобы изменить флаги состояния открытого файла, сначала с помощью вызова fcntl() извлекаются копии существующих флагов, затем изменяются нужные разряды и, наконец, делается еще один вызов fcntl() для обновления флагов. Таким образом, чтобы включить флаг O_APPEND, можно написать следующий код:
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
errExit("fcntl");
flags |= O_APPEND;
if (fcntl(fd, F_SETFL, flags) == -1)
errExit("fcntl");
К этому моменту у вас могло создаться впечатление, что между файловым дескриптором и открытым файлом существует соотношение «один к одному». Но это не так. Есть весьма полезная возможность иметь сразу несколько дескрипторов, ссылающихся на один и тот же открытый файл. Эти файловые дескрипторы могут быть открыты в одном и том же или в разных процессах.
Чтобы разобраться в происходящем, нужно изучить три структуры данных, обслуживаемые ядром:
• таблицу дескрипторов файлов для каждого процесса;
• общесистемную таблицу дескрипторов открытых файлов;
• таблицу индексных дескрипторов файловой системы.
Для каждого процесса ядро поддерживает таблицу дескрипторов открытых файлов. Каждая запись в этой таблице содержит информацию об одном файловом дескрипторе, включая:
• набор флагов, управляющих работой файлового дескриптора (такой флаг всего один — флаг закрытия при выполнении — close-on-exec, и он будет рассмотрен в разделе 27.4);
• ссылку на дескриптор открытого файла.
Ядро обслуживает общесистемную таблицу всех дескрипторов открытых файлов. (Она иногда называется таблицей открытых файлов, а записи в ней — дескрипторами открытых файлов.) В дескрипторе открытого файла хранится вся информация, относящаяся к открытому файлу, включая:
• текущее файловое смещение (обновляемое системными вызовами read() и write() или явно изменяемое с помощью системного вызова lseek());
• флаги состояния при открытии файла (то есть аргумент flags системного вызова open());
• режим доступа к файлу (только для чтения, только для записи или для чтения и записи, согласно установкам для системного вызова open());
• установки, относящиеся к вводу-выводу, управляемому сигналами (см. раздел 59.3);
• ссылку на индексный дескриптор для этого файла.
У каждой файловой системы есть таблица индексных дескрипторов для всех размещенных в ней файлов. Структура индексных дескрипторов и в целом файловых систем более подробно рассматривается в главе 14. А сейчас следует отметить, что индексный дескриптор для каждого файла включает такую информацию:
• тип файла (например, обычный файл, сокет или FIFO-устройство) и права доступа;
• указатель на список блокировок, удерживаемых на этом файле;
• разные свойства файла, включая его размер и метки времени, связанные с различными типами файловых операций.
Здесь мы не учитываем разницу между представлением индексного дескриптора на диске и в памяти. В индексном дескрипторе на диске записываются постоянные атрибуты, такие как его тип, права доступа и отметки времени. Когда происходит доступ к файлу, создается копия индексного дескриптора, хранящаяся в памяти, и в эту версию индексного дескриптора записывается количество файловых дескрипторов, ссылающихся на индексный дескриптор, и главные и второстепенные идентификаторы устройства, из которого был скопирован индексный дескриптор. В индексный дескриптор, хранящийся в памяти, также записываются различные недолговечные атрибуты, связанные с файлом при его открытии, например блокировки файлов.
Связь между дескрипторами файлов, дескрипцией открытых файлов и индексными дескрипторами показана на рис. 5.2. На этой схеме у двух процессов имеется несколько дескрипторов открытых файлов.
В процессе А два дескриптора — 1 и 20 — ссылаются на один и тот же дескриптор открытого файла (с пометкой 23). Такая ситуация может возникать в результате вызова dup(), dup2() или fcntl() (см. раздел 5.5).