Файловый дескриптор

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 01:40, 29 мая 2016.

Дескриптор файла - это целое число без знака, с помощью которого процесс обращается к открытому файлу. Количество дескрипторов файлов, доступных процессу, ограничено параметром /OPEN_MAX, заданным в файле sys/limits.h. Кроме того, количество дескрипторов файлов можно задать с помощью флага -n команды ulimit. Дескрипторы файлов создаются при выполнении функций open, pipe, creat и fcntl. Обычно каждый процесс работает с уникальным набором дескрипторов. Однако эти же дескрипторы могут применяться и дочерними процессами, созданными с помощью функции fork. Кроме того, дескрипторы можно скопировать с помощью функций fontal, dup и dup2.

Все открытые файлы ссылаются к ядру через так называемые файловые дескрипторы. Файловый дескриптор — это неотрицательное целое число. Когда мы открываем существующий файл и создаем новый файл, ядро возвращает процессу файловый дескриптор.

По умолчанию UNIX-шеллы связывают файловый дескриптор 0 со стандартным вводом процесса (терминал), файловый дескриптор 1 — со стандартным выводом (терминал), и файловый дескриптор 2 — со стандартной ошибкой (то есть то куда выводятся сообщения об ошибках). Это соглашение соблюдается многими UNIX-шеллами и многими приложениями — и в ни коем случае не является составной частью ядра.

Стандарт POSIX.1 заменил «магические числа» 0,1,2 символическими константами STDIN_FILENO, STDOUT_FILENO и STDERR_FILENO соответственно.

Файловые дескрипторы могут принимать значения от 0 до OPEN_MAX. Старые версии UNIX имели верхний предел до 19, позволяя одному процессу открывать до 20 файлов. Сейчас это значение увеличено до нескольких тысяч.

Дескрипторы файлов выполняют роль индексов таблицы дескрипторов, которая расположена в области u_block и создается ядром для каждого процесса. Чаще всего процесс получает дескрипторы с помощью операций open и creat, а также путем наследования от родительского процесса. При выполнении операции fork таблица дескрипторов копируется для дочернего процесса. В результате дочерний процесс получает право обращаться к файлам родительского процесса.

Таблицы дескрипторов файлов и системные таблицы открытых файлов

Структуры данных, содержащие список открытых файлов и список дескрипторов файлов, позволяют отслеживать обращения процессов к файлам и гарантировать целостность данных.

Таблица дескрипторов файлов

Преобразует индексы таблицы (дескрипторы файлов) в указатели на открытые файлы. Для каждого процесса в области u_block создается своя собственная таблица дескрипторов. Каждая запись такой таблицы содержит следующие поля: поле флагов и указатель на файл. Допустимо не более OPEN_MAX дескрипторов файлов. Таблица дескрипторов файлов имеет следующую структуру:

struct ufd
{
     struct file *fp;
     int flags;
} *u_ufd

Таблица открытых файлов

Содержит записи с информацией обо всех открытых файлах. В записи этой таблицы хранится текущее смещение указателя в файле, которое используется во всех операциях чтения и записи в файл, а также режим открытия файла (O_RDONLY, O_WRONLY или O_RDWR). В структуре таблицы открытых файлов хранится смещение указателя в файле. При выполнении операции чтения-записи система выполняет неявный сдвиг указателя. Например, при чтении или записи x байт указатель также будет перемещен на x байт. Для изменения положения указателя в файлах с прямым доступом применяется функция seek. Для потоковых файлов (например, каналов и сокетов) понятие смещения не поддерживается, так как произвольный доступ к этим файлам невозможен.

Управление дескрипторами файлов

Поскольку с файлами может работать несколько пользователей, необходимо, чтобы связанные процессы работали с общим указателем смещения, а независимые процессы - с собственным указателем смещения в файле. В записи таблицы открытых файлов содержится счетчик обращений к файлу, отражающий число дескрипторов, соответствующих данному файлу.

Несколько обращений к файлу может потребоваться в следующих случаях:

  • Файл открыт еще одним процессом
  • Дочерний процесс унаследовал дескрипторы файлов, открытых родительским процессом
  • Дескриптор файла скопирован с помощью функции fcntl или dup

Совместная работа с открытыми файлами

При выполнении каждой операции открытия в таблицу открытых файлов добавляется запись. Это гарантирует, что каждый процесс будет работать со своим указателем в файле. Такой подход позволяет сохранить целостность данных.

При копировании дескриптора два процесса начинают работать с одним и тем же указателем. В этом случае оба процесса могут попытаться одновременно обратиться к файлу, при этом данные будут считаны или записаны не последовательно.

Копирование дескрипторов файлов

Существуют следующие способы копирования дескрипторов файлов: функция dup или dup2, функция fork и функция fcntl.

Функции dup и dup2

Функция dup создает копию дескриптора файла. Копия создается в пустой строке пользовательской таблицы дескрипторов, содержащей исходный дескриптор. При вызове dup увеличивается значение счетчика обращений к файлу в записи таблицы открытых файлов и возвращается новый дескриптор файла.
Функция dup2 находит запрошенный дескриптор и закрывает связанный с ним файл, если он открыт. С ее помощью можно указать конкретную запись таблицы, в которую должен быть скопирован дескриптор.

Функция fork

Функция fork создает дочерний процесс, который наследует все дескрипторы файлов родительского процесса. После этого дочерний процесс запускает новый процесс. Унаследованные дескрипторы с флагом Закрыть при exec, установленным с помощью fcntl, будут закрыты.


Функция fcntl

Функция fcntl позволяет работать со структурой данных о файле и с дескрипторами открытых файлов. Она позволяет выполнять следующие операции над дескрипторами:
  • Копировать дескриптор файла (аналогично функции dup).
  • Получать или устанавливать значение флага Закрыть при exec.
  • Выключать режим объединения дескрипторов в блоки.
  • Включать режим добавления данных в конец файла (O_APPEND).
  • Включать отправку процессам сигнала о разрешении ввода-вывода.
  • Устанавливать и получать ИД процесса или группы процессов для отправки SIGIO.
  • Закрывать все дескрипторы файлов.

Стандартные дескрипторы файлов

При запуске программы в оболочке открывается три дескриптора 0, 1 и 2. По умолчанию с ними связаны следующие файлы:

0 Стандартный ввод.
1 Стандартный вывод.
2 Стандартный вывод сообщений об ошибках.

Перечисленные дескрипторы файлов связаны с терминалом. Это означает, что при чтении данных из файла с дескриптором 0 программа получает ввод с терминала, а при записи данных в файлы с дескрипторами 1 и 2 они выводятся на терминал. При открытии других файлов дескрипторы присваиваются в порядке возрастания.

Если ввод-вывод перенаправляется с помощью операторов < (знак меньше) или > (знак больше), то стандартные дескрипторы связываются с другими файлами. Например, следующая команда связывает дескрипторы файлов 0 и 1 с необходимыми файлами (по умолчанию эти дескрипторы связаны с терминалом).

           prog < FileX > FileY

В данном примере дескриптор 0 будет связан с файлом FileX, а дескриптор 1 - с файлом FileY. Дескриптор 2 не будет изменен. Программе достаточно знать, что дескриптор 0 представляет файл ввода, а дескрипторы 1 и 2 - файлы вывода. Информация о том, с какими конкретно файлами связаны эти дескрипторы, ей не нужна.

В следующем примере программы продемонстрировано перенаправление стандартного вывода:

#include <fcntl.h>
#include <stdio.h>

void redirect_stdout(char *);

main()
{
       printf("Hello world\n");       /* печать в стандартный вывод */
       fflush(stdout);
       redirect_stdout("foo");        /*перенаправление стандартного вывода*/
       printf("Hello to you too, foo\n"); /*печать в файл foo */
       fflush(stdout);
}

void
redirect_stdout(char *filename)
{
        int      fd;
        if ((fd = open(filename,O_CREAT|O_WRONLY,0666)) < 0) /*открытие нового файла*/
        {
                perror(filename);
                exit(1);
        }
        close(1);                       /*закрытие стандартного вывода */
        if (dup(fd) !=1)                /*присвоение новому дескриптору значения 1*/
        {
                fprintf(stderr,"Unexpected dup failure\n");
                exit(1);
        }
        close(fd);                       /*закрытие ненужного исходного fd*/
}

При получении запроса на дескриптор выделяется первый свободный дескриптор из таблицы дескрипторов (дескриптор с наименьшим номером). Однако с помощью функции dup файлу можно присвоить любой дескриптор.

Ограничение на число дескрипторов файлов

Максимальное число дескрипторов, которое может использоваться в одном процессе, ограничено. Значение по умолчанию указывается в файле /etc/security/limits и обычно равно 2000. Для изменения ограничения можно воспользоваться командой ulimit или функцией setrlimit. Максимальное число определяется константой OPEN_MAX.

Источники