Что такое дескриптор файла

Обновлено: 04.07.2024

Для большинства операционных систем файловый дескриптор (FD) представляет собой небольшое неотрицательное целое число, помогающее идентифицировать открытый файл в процессе при использовании ресурсов ввода/вывода, таких как сетевые сокеты или каналы. В некотором смысле его можно рассматривать как индексную таблицу открытых файлов. Когда есть операции чтения, записи или закрытия файла, одним из рассматриваемых входных параметров является файловый дескриптор. Дескрипторы файлов являются важным компонентом интерфейса прикладного программирования POSIX и обеспечивают примитивный низкоуровневый интерфейс для операций ввода и вывода.

Techopedia объясняет файловый дескриптор (FD)

Ядро создает файловый дескриптор всякий раз, когда сталкивается с открытым вызовом. Во многих отношениях шлюз в абстракции ядра базового оборудования можно рассматривать как файловые дескрипторы. В операционной системе Unix стандартный ввод представлен дескриптором файла 0, стандартный вывод представлен дескриптором файла 1, а стандартный файл ошибок представлен дескриптором файла 2. Другими словами, в соответствии с тремя стандартными потоками каждый процесс UNIX будет иметь три стандартных файловых дескриптора. И потоки, и файловые дескрипторы могут представлять соединение с устройством, однако для управления конкретными устройствами необходимо использовать файловые дескрипторы. В большинстве операционных систем, таких как UNIX, дескрипторы файлов представлены как объекты типа «int». Дескриптор файла используется ядром в качестве индекса в таблице описания файлов, чтобы определить, какой процесс первоначально открыл конкретный файл, а затем разрешить выполнение запрошенных операций на открытом устройстве или файле.

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

Одна из первых вещей, которую узнают программисты UNIX, это то, что каждая работающая программа запускается с тремя уже открытыми файлами:

Описательное имяКороткое имяНомер файлаОписание
Стандартный вstdin0Ввод с клавиатуры
Стандартный выходstdout1Вывод в консоль
Стандартная ошибкаstderr2Вывод ошибки на консоль

Стандартные файлы, открываемые любой программой UNIX.

В связи с этим возникает вопрос о том, что представляет собой открытый файл. Значение, возвращаемое вызовом open, называется файловым дескриптором и, по сути, является индексом в массиве открытых файлов, хранимом ядром.

Файловые дескрипторы связывают абстракцию, предоставляемую драйверами устройств, с файлом интерфейс, предоставляемый пользователю». /><br /></p>
<p>Файловые дескрипторы — это индекс в таблице файловых дескрипторов, хранимой ядром. Ядро создает дескриптор файла в ответ на вызов open и связывает дескриптор файла с некоторой абстракцией нижележащего файлового объекта, будь то реальное аппаратное устройство, файловая система или что-то еще. Следовательно, вызовы чтения или записи процесса, которые ссылаются на этот дескриптор файла, направляются ядром в нужное место, чтобы в конечном итоге сделать что-то полезное.</p>
<p>Короче говоря, файловый дескриптор — это ворота в абстракции ядра базового оборудования. Общий вид абстракции для физических устройств показан на Рисунке 1.3, «Абстракция».</p>
<p>Начиная с самого нижнего уровня, операционная система требует от программиста создания <em>драйвера устройства</em>, чтобы иметь возможность взаимодействовать с аппаратным устройством.Этот драйвер устройства записывается в API, предоставляемом ядром, как в примере 1.2, «Абстракция в include/linux/virtio.h»; драйвер устройства предоставляет ряд функций, которые вызываются ядром в ответ на различные требования. В приведенном выше упрощенном примере мы видим, что драйверы предоставляют функцию чтения и записи, которая будет вызываться в ответ на аналогичные операции с файловым дескриптором. Драйвер устройства знает, как преобразовать эти общие запросы в конкретные запросы или команды для конкретного устройства.</p>
<p>Чтобы обеспечить абстракцию пользовательского пространства, ядро ​​предоставляет файловый интерфейс через так называемый <em>уровень устройства</em> . Физические устройства на хосте представлены файлом в специальной файловой системе, такой как /dev. В UNIX-подобных системах так называемые <em>узлы-устройства</em> имеют так называемые <em>основной</em> и <em>дополнительный</em> номера, которые позволяют ядру ассоциировать определенные узлы с их базовым драйвером. Их можно определить с помощью ls, как показано в Примере 1.3, «Пример старших и младших чисел».</p>
<p>Это подводит нас к файловому дескриптору, который является дескриптором, который пользовательское пространство использует для связи с базовым устройством. В широком смысле, когда файл открывается, ядро ​​использует информацию о пути для сопоставления дескриптора файла с чем-то, что обеспечивает соответствующий API для чтения и записи и т. д. Когда это открытие предназначено для устройства (/dev/sr0 выше), старший и младший номера открытого узла устройства предоставляют информацию, необходимую ядру для поиска правильного драйвера устройства и завершения сопоставления. После этого ядро ​​будет знать, как направлять дальнейшие вызовы, такие как чтение, на базовые функции, предоставляемые драйвером устройства.</p>
<p>Файл, не относящийся к устройству, работает аналогично, хотя между ними больше слоев. Абстракцией здесь является <em>точка монтирования</em> ; монтирование файловой системы имеет двойную цель: настроить сопоставление, чтобы файловая система знала базовое устройство, предоставляющее хранилище, а ядро ​​​​знало, что файлы, открытые в этой точке монтирования, должны быть направлены драйверу файловой системы. Как и драйверы устройств, файловые системы записываются в определенный универсальный API файловой системы, предоставляемый ядром.</p>
<p>На самом деле существует множество других слоев, которые усложняют картину в реальной жизни. Например, ядро ​​приложит огромные усилия, чтобы кэшировать как можно больше данных с дисков в свободной памяти; это дает много преимуществ в скорости. Он также попытается организовать доступ к устройствам наиболее эффективным способом; например, попытка заказать доступ к диску, чтобы гарантировать, что данные, хранящиеся физически близко друг к другу, извлекаются вместе, даже если запросы не поступали в последовательном порядке. Кроме того, многие устройства относятся к более общему классу, например устройства USB или SCSI, которые предоставляют свои собственные уровни абстракции для записи. Таким образом, вместо прямой записи на устройства файловые системы будут проходить через эти многочисленные слои. Понимание ядра означает понимание того, как эти многочисленные API взаимодействуют и сосуществуют.</p>
<h3>Оболочка</h3>
<p>Оболочка — это шлюз для взаимодействия с операционной системой. Будь то bash, zsh, csh или любая из множества других оболочек, у всех у них по сути есть только одна основная задача — позволить вам выполнять программы (вы начнете понимать, как на самом деле это делает оболочка, когда мы поговорим о некоторых внутренних компонентах операционной системы позже).</p>
<p>Оболочки делают гораздо больше, чем просто запускают программу. Они обладают мощными возможностями перенаправления файлов, позволяют выполнять несколько программ одновременно и завершать программы по сценариям. Все они возвращаются к идиоме <em>все есть файл</em>.</p>
<h4>Перенаправление</h4>
<p>Часто мы не хотим, чтобы стандартные файловые дескрипторы, упомянутые в разделе «Файловые дескрипторы», указывали на их места по умолчанию. Например, вы можете захотеть записать весь вывод программы в файл на диске или, в качестве альтернативы, заставить ее читать свои команды из файла, который вы подготовили ранее. Другой полезной задачей может быть передача вывода одной программы на вход другой. В операционной системе все это и многое другое облегчает оболочка.</p>
<table style=ИмяКомандаОписаниеПример< tbody>Перенаправить в файл > имя_файла Возьмите весь вывод из стандарта и поместите его в имя файла .Обратите внимание, что использование >> добавит файл, а не перезапишет его. ls > имя файла Чтение из файла имя файла Скопировать все данные из файла на стандартный ввод программы echo труба program1 | program2 Взять все из стандарта из программы1 и передать на стандартный ввод программы2 ls | больше

Реализация канала

Реализация ls | more — это еще один пример силы абстракции. В основном здесь происходит то, что вместо связывания дескриптора файла для стандартного вывода с каким-либо базовым устройством (например, консолью для вывода на терминал) дескриптор указывает на буфер в памяти, предоставленный ядром. обычно называют трубой. Хитрость здесь в том, что другой процесс может связать свой стандартный вход с другой стороной этого же буфера и эффективно использовать выходные данные другого процесса. Это показано на рис. 1.4, «Труба в действии».

Канал — это буфер в памяти, предоставляемый ядром, который позволяет результат одного процесса, который будет потребляться как вход для другого». /><br /></p>
<p>Канал — это буфер в памяти, который соединяет два процесса вместе. файловые дескрипторы указывают на объект канала, который буферизует отправленные ему данные (через запись ) для <em>слива</em> (через чтение ).</p>
<p>Записи в канал сохраняются ядром до тех пор, пока соответствующее чтение с другой стороны <em>не истощит</em> буфер. Это очень мощная концепция и одна из основных форм <em>межпроцессного взаимодействия</em> или IPC в UNIX-подобных операционных системах. Канал позволяет больше, чем просто передавать данные; он может действовать как сигнальный канал. Если процесс читает пустой канал, он по умолчанию <em>блокируется</em> или переходит в спящий режим до тех пор, пока не будут доступны какие-либо данные (более подробно это обсуждается в Главе 5, <em>Процесс<). /эм>). Таким образом, два процесса могут использовать конвейер для сообщения о том, что было предпринято какое-то действие, просто записав байт данных; важны не сами данные, а простое присутствие <em>каких-либо</em> данных в канале, что может сигнализировать о сообщении. Скажем, например, один процесс запрашивает, чтобы другой распечатал файл — это займет некоторое время. Два процесса могут установить канал между собой, где запрашивающий процесс читает пустой канал; будучи пустым, этот вызов блокируется, и процесс не продолжается. Как только печать выполнена, другой процесс может записать сообщение в канал, что эффективно пробуждает запрашивающий процесс и сигнализирует о том, что работа выполнена.</p>
<p>Разрешение процессам передавать данные друг другу, как это, приводит к еще одной распространенной идиоме UNIX небольших инструментов, выполняющих одну конкретную задачу. Объединение этих небольших инструментов в цепочку обеспечивает гибкость, которую зачастую не может обеспечить один монолитный инструмент.</p>
<p>Каким может быть более упрощенное описание файловых дескрипторов по сравнению с Википедией? Зачем они нужны? Скажем, взять в качестве примера процессы оболочки и как это применимо к нему?</p>
<p>Содержит ли таблица процессов более одного файлового дескриптора. Если да, то почему?</p>
<p>Как насчет концепций stdin stdout stderr и т. д.? У меня есть экземпляр, например, открытый процесс браузера, и он открыл некоторые временные файлы для отображения моего html. Процесс использует один и тот же fd для чтения/записи? Также таблица процессов. в нем есть такие записи, как указатель fd0 указатель fd1 указатель fd2. значит ли это, что все эти файлы находятся в оперативной памяти? Зачем еще указатели?</p>
<p>Когда вы открываете файл, ОС создает поток для этого файла и подключает этот поток к открытому файлу, дескриптор фактически представляет этот поток. Точно так же есть некоторые потоки по умолчанию, созданные ОС. Эти потоки подключаются к вашему терминалу вместо файлов. Поэтому, когда вы пишете что-то в терминале, оно переходит в поток stdin и в ОС. И когда вы пишете команду «ls» на терминале, ОС записывает вывод в поток stdout. поток stdout подключен к терминалу вашего монитора, поэтому вы можете видеть вывод там.</p>
<p>Что касается примера с браузером, нет необходимости, чтобы браузер держал файлы открытыми. Это зависит от реализации браузера, но в большинстве случаев браузер открывает временный файл, записывает файл и закрывает файл, поэтому нет необходимости открывать файл, даже если веб-страница открыта. И дескриптор просто содержит информацию о файле и не обязательно хранит файл в оперативной памяти. Когда вы читаете данные из дескриптора, ОС считывает данные с жесткого диска.Информация в файловом дескрипторе просто представляет расположение файла на жестком диске и т. д..</p>
<p>Описатель файла в файл не является однозначным сопоставлением. Я мог бы открыть() один и тот же файл 4 раза и получить 4 разных файловых дескриптора. Каждый из них может использоваться (в зависимости от флагов, переданных в open()) для чтения, записи или того и другого. Что касается того, живет ли файл в оперативной памяти или на диске — это скрыто от вас ядром и его различными кэшами. В конечном итоге то, что есть в кеше, будет соответствовать тому, что есть на диске (для записи), и ядро ​​не вернется на диск, для чтения, если данные уже есть в кеше.</p>
<h2>12 ответов 12</h2>
<p>Проще говоря, когда вы открываете файл, операционная система создает запись для представления этого файла и хранения информации об этом открытом файле. Таким образом, если в вашей ОС открыто 100 файлов, то в ОС будет 100 записей (где-то в ядре). Эти записи представлены целыми числами, такими как (. 100, 101, 102.). Этот номер записи является дескриптором файла. Таким образом, это просто целое число, которое однозначно представляет открытый файл для процесса. Если ваш процесс открывает 10 файлов, в таблице процессов будет 10 записей для файловых дескрипторов.</p>
<p>Аналогично, когда вы открываете сетевой сокет, он также представляется целым числом и называется дескриптором сокета. Надеюсь, вы понимаете.</p>
<p>Кроме того, именно поэтому у вас могут закончиться файловые дескрипторы, если вы откроете много файлов одновременно. Это предотвратит запуск систем *nix, поскольку они постоянно открывают дескрипторы для заполнения /proc.</p>
<p>@ErbenMo: Нет, это может быть не то же самое. Когда вы открываете файл, операционная система назначит доступный FD, а когда вы его закроете, ОС освободит FD и может назначить этот FD другому файлу, открытому после этого. Это способ операционной системы отслеживать открытые файлы, и это не имеет ничего общего с конкретным файлом.</p>
<p>Значит, это просто целое число, которое однозначно представляет открытый файл в операционной системе." Это неверно. Это целое число однозначно представляет открытый файл в процессе. Дескриптор файла 0, например, будет представлять один открытый файл в одном процессе и совершенно другой открытый файл в другом процессе.

@Tayyab: Думаю, ты ошибаешься. Файловые дескрипторы 0, 1 и 2 — это стандартный ввод, стандартный вывод и стандартная ошибка для каждого запущенного процесса. Успешный первоначальный вызов open() даст вам файловый дескриптор 3, даже если другой запущенный процесс имеет файловый дескриптор 3. См. определение POSIX для open(): «Функция open() должна возвращать файловый дескриптор для именованный файл с наименьшим файловым дескриптором, который в данный момент не открыт для этого процесса». (выделение добавлено).

@KeithThompson: Да, вы правы. На самом деле речь идет об уровне абстракции. На самом деле поддерживаются две таблицы, первая из которых предназначена для каждого процесса, а вторая — для всей системы. FD в таблице для каждого процесса (т.е. fdtable) не уникален для всей системы. Однако он сопоставляется с таблицей v-node, которая содержит уникальные записи для всей системы. Поэтому, когда вы вызываете функции fopen() и fileno() для проверки дескриптора, вы можете получить один и тот же номер FD в двух разных процессах, потому что он возвращает индекс fdtable для каждого процесса. Спасибо, что подняли тему!!

Дескриптор файла — это непрозрачный дескриптор, который используется в интерфейсе между пользователем и пространством ядра для идентификации ресурсов файла/сокета. Поэтому, когда вы используете open() или socket() (системные вызовы для взаимодействия с ядром), вам предоставляется файловый дескриптор, который является целым числом (на самом деле это индекс в структуре процессов u, но это не важно). ). Поэтому, если вы хотите напрямую взаимодействовать с ядром, используя системные вызовы read() , write() , close() и т. д., вы используете дескриптор файла.

На системные вызовы накладывается слой абстракции, которым является интерфейс stdio. Это обеспечивает больше функциональных возможностей/возможностей, чем базовые системные вызовы. Для этого интерфейса непрозрачный дескриптор, который вы получаете, представляет собой FILE* , который возвращается вызовом fopen(). Существует множество функций, использующих интерфейс stdio fprintf() , fscanf() , fclose() , которые упрощают вашу жизнь. В C stdin , stdout и stderr являются FILE* , которые в UNIX соответственно сопоставляются с файловыми дескрипторами 0 , 1 и 2 .

Услышьте это из первых уст: APUE (Ричард Стивенс).
Для ядра все открытые файлы называются файловыми дескрипторами. Дескриптор файла — это неотрицательное число.

Когда мы открываем существующий файл или создаем новый файл, ядро ​​возвращает процессу дескриптор файла. Ядро поддерживает таблицу всех дескрипторов открытых файлов, которые используются. Распределение файловых дескрипторов обычно последовательное, и они назначаются файлу как следующий свободный файловый дескриптор из пула свободных файловых дескрипторов. Когда мы закрываем файл, дескриптор файла освобождается и становится доступным для дальнейшего выделения.
Подробнее см. на этом изображении:

Два процесса

Когда мы хотим прочитать или записать файл, мы идентифицируем файл с дескриптором файла, который был возвращен вызовом функции open() или create(), и используем его в качестве аргумента для чтения() или записи(). .
Согласно соглашениям, системные оболочки UNIX связывают дескриптор файла 0 со стандартным вводом процесса, дескриптор файла 1 со стандартным выводом и дескриптор файла 2 со стандартной ошибкой.
Описатель файла находится в диапазоне от 0 до OPEN_MAX. Максимальное значение дескриптора файла можно получить с помощью ulimit -n . Для получения дополнительной информации прочитайте 3-ю главу книги APUE.

Что такое файловый дескриптор?
Файловый дескриптор — это целое число, которое однозначно идентифицирует открытый файл процесса.

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

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


Стандартные файловые дескрипторы: при запуске любого процесса автоматически открывается таблица fd (файловый дескриптор) 0, 1, 2 таблицы файловых дескрипторов этого процесса (по умолчанию) (по умолчанию) каждый из этих трех файловых дескрипторов ссылается на запись в таблице файлов для файла с именем /dev. /терминал

/dev/tty: заместитель терминала в памяти
Терминал: комбинация клавиатуры и видеоэкрана


Чтение со стандартного ввода => чтение с fd 0: Всякий раз, когда мы пишем какой-либо символ с клавиатуры, он считывается со стандартного ввода через fd 0 и сохраняется в файл с именем /dev/tty.
Запись на стандартный вывод => запись в fd 1: Всякий раз, когда мы видим какой-либо вывод на видеоэкран, он поступает из файла с именем /dev/tty и записывается в стандартный вывод на экране через fd 1.
Запись в стандартный вывод => запись в fd 2: Мы видим любую ошибку в экран видео, он же из этого файла пишет в stderr в screen через fd 2.

Системные вызовы ввода/вывода

В основном существует 5 типов системных вызовов ввода/вывода:

<р>1. Создать: используется для создания нового пустого файла.

  • имя файла: имя файла, который вы хотите создать
  • режим: указывает права доступа к новому файлу.
  • возвратить первый неиспользуемый файловый дескриптор (обычно 3 при первом создании использования в процессе, потому что 0, 1, 2 fd зарезервированы)
  • вернуть -1 при ошибке
  • Создать новый пустой файл на диске
  • Создать запись в таблице файлов
  • Установить первый неиспользуемый файловый дескриптор так, чтобы он указывал на запись в таблице файлов
  • Возвращает использованный файловый дескриптор, -1 в случае ошибки
<р>2. open: используется для открытия файла для чтения, записи или того и другого.

Читайте также: