Проверка режима доступа к файлу происходит немного сложнее, поскольку константы O_RDONLY (0), O_WRONLY (1) и O_RDWR (2) не соответствуют отдельным разрядам флагов состояния открытого файла. По этой причине на значение флагов накладывается маска с помощью константы O_ACCMODE, а затем проводится проверка на равенство одной из констант:
accessMode = flags & O_ACCMODE;
if (accessMode == O_WRONLY || accessMode == O_RDWR)
printf("file is writable\n");
Команду F_SETFL системного вызова fcntl() можно использовать для изменения некоторых флагов состояния открытого файла. К ним относятся O_APPEND, O_NONBLOCK, O_NOATIME, O_ASYNC и O_DIRECT. Попытки изменить другие флаги игнорируются. (В некоторых других реализациях UNIX системному вызову fcntl() разрешается изменять и другие флаги, например O_SYNC.)
Возможность использования вызова 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);
• ссылку на индексный дескриптор для этого файла.
У каждой файловой системы есть таблица
• тип файла (например, обычный файл, сокет или FIFO-устройство) и права доступа;
• указатель на список блокировок, удерживаемых на этом файле;
• разные свойства файла, включая его размер и метки времени, связанные с различными типами файловых операций.