int new_fd = fcntl(old_fd, F_DUPFD, 7);
/* Возвращаемое значение между 7 и максимумом или неудача */
int new_fd = dup2(old_fd, 7);
/* Возвращаемое значение 7 или неудача */
Вы можете имитировать поведение
dup()
, которая возвращает наименьший свободный дескриптор файла, использовав '
fcntl(old_fd, F_DUPED, 0)
'.
Если вы помните, что дескрипторы файлов являются просто индексами внутренней таблицы, работа этой функции должна быть ясна. Третий аргумент просто предоставляет индекс, с которого ядро должно начать поиск неиспользуемого дескриптора файла.
Использовать ли в собственном коде
fcntl()
с
F_DUPED
или
dup()
или
dup2()
, в значительной степени является делом вкуса. Все три функции API являются частью POSIX и широко поддерживаются. У нас легкое пристрастие к
dup()
и
dup2()
, поскольку они более специфичны в своих действиях, поэтому являются самодокументирующимися. Но поскольку все они довольно просты, эта аргументация может вас не убедить.
9.4.3.3. Работа с флагами статуса файла и режимами доступа
В разделе 4.6.3 «Возвращаясь к
open()
» мы предоставили полный список флагов O_
xx, которые принимает
open()
. POSIX разбивает их по функциям, классифицируя в соответствии с табл. 9.4.
Таблица 9.4. Флаги O_xx для
open()
,
creat()
и
fcntl()
Категория | Функции | Флаги |
Доступ к файлу | open() , fcntl()
| O_RDONLY , O_RDWR , O_WRONLY
|
Создание файла | open()
| O_CREAT , O_EXCL , O_NOCTTY , O_TRUNC
|
Статус файла | open() , fcntl()
| O_APPEND , O_DSYNC , O_NONBLOCK , O_RSYNC , O_SYNC
|
Помимо первоначальной установки различных флагов с помощью
open()
, вы можете использовать
fcntl()
для получения текущих установок, а также их изменения. Это осуществляется с помощью значений
cmd
F_GETFL
и
F_SETFL
соответственно. Например, вы можете использовать эти команды для изменения установки неблокирующего флага,
O_NONBLOCK
, подобным образом:
int fd_flags;
if ((fd_flags = fcntl(fd, F_GETFL)) < 0)
/* обработать ошибку */
if ((fd_flags & O_NONBLOCK) != 0) { /* Установлен неблокирующий флаг */
fd_flags &= ~O_NONBLOCK; /* Сбросить его */
if (fcntl(fd, F_SETFL, fd_flags) != 0) /* Дать ядру новое значение */
/* обработать ошибку */
}
Помимо самих режимов именованная константа
O_ACCMODE
является маской, которую вы можете использовать для выделения из возвращаемого значения режимов прав доступа.
fd_flags = fcntl(fd, F_GETFL);
switch (fd_flags & O_ACCESS) {
case O_RDONLY:
/* ...действия только для чтения... */
break;
case O_WRONLY:
/* ...действия только для записи... */
break;
case O_RDWR:
/* ...действия для чтения и записи... */
break;
}
POSIX требует, чтобы
O_RDONLY
,
O_RDWR
и
O_WRONLY
были побитово различными, таким образом, гарантируется, что код, подобный только что показанному, будет работать и является простым способом определения того, как был открыт произвольный дескриптор файла.
Используя
F_SETFL
вы можете также изменить эти режимы, хотя по-прежнему применяется проверка прав доступа. Согласно справочной странице GNU/Linux
fcnlt(2) флаг
O_APPEND
не может быть сброшен, если он использовался при открытии файла.
9.4.3.4. Неблокирующий ввод/вывод для каналов и FIFO
Ранее для описания способа работы каналов мы использовали сравнение с двумя людьми, моющими и вытирающими тарелки с использованием сушилки; когда сушилка заполняется, останавливается моющий, а когда она пустеет, останавливается вытирающий. Это блокирующее поведение: производитель или потребитель блокируются в вызове
write()
или
read()
, ожидая либо освобождения канала, либо появления в нем данных.
В действительности человек, ожидающий опустения или заполнения сушилки, не должен просто неподвижно стоять.[101] Вместо этого незанятый супруг мог бы пойти и найти другую работу по кухне (такую, как подметание всех крошек за детьми на полу), пока сушилка снова не будет готова.
На языке Unix/POSIX эта концепция обозначается термином неблокирующий ввод/вывод, т.е. запрошенный ввод/вывод либо завершается, либо возвращает значение ошибки, указывающее на отсутствие данных (для читающего) или отсутствие места (для записывающего). Неблокирующий ввод/вывод применяется к каналам и FIFO, а не к обычным файлам на диске. Он может применяться также и к определенным устройствам, таким как терминалы, и к сетевым соединениям, обе эти темы выходят за рамки данной книги.
С функцией
open()
может использоваться флаг
O_NONBLOCK
для указания неблокирующего ввода/вывода, он может быть установлен и сброшен с помощью
fcntl()
. Для
open()
и
read()
неблокирующий ввод/вывод прост.