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

Обновлено: 07.07.2024

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

содержит ли таблица процессов более одного файлового дескриптора. Если да, то почему?

простыми словами, когда вы открываете файл, операционная система создает запись для представления этого файла и хранения информации об этом открытом файле. Поэтому, если в вашей ОС открыто 100 файлов, то в ОС будет 100 записей (где-то в ядре). Эти записи представлены целыми числами, такими как (. 100, 101, 102. ). Этот номер записи является дескриптором файла. Таким образом, это просто целое число, которое однозначно представляет открытый файл в операционной системе. Если процесс открывает 10 файлы тогда ваша таблица процессов будет иметь 10 записей для файловых дескрипторов.

аналогично, когда вы открываете сетевой сокет, он также представлен целым числом и называется дескриптором сокета. Надеюсь, вы понимаете.

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

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

Two Process

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

когда мы хотим прочитать или записать файл, то найдите файл с дескриптором файла, который был возвращен открыть() или create () вызов функции и используйте его в качестве аргумента либо read () или write ().
По соглашению, системные оболочки UNIX связывают файловый дескриптор 0 с Стандартный Ввод процесса, файловый дескриптор 1 с Стандартный Вывод, и файл desciptor 2 с Стандартная Ошибка.
Файловый дескриптор колеблется от 0 до OPEN_MAX.
Для получения дополнительной информации пройдите 3-ю главу книги APUE.

больше очков в отношении File Descriptor :

File Descriptors (FD) неотрицательные целые числа (0, 1, 2, . ) которые связаны с открытыми файлами.

0, 1, 2 стандартные FD's, что соответствует STDIN_FILENO , STDOUT_FILENO и STDERR_FILENO (определена в unistd.h ) открывается по умолчанию от имени shell при запуске программы.

FD распределяются в последовательном порядке, что означает наименьшее возможное нераспределенное целое значение.

FD для определенного процесса можно увидеть в /proc/$pid/fd (в системах на базе Unix).

любая операционная система имеет запущенные процессы (p), скажем p1, p2, p3 и так далее. Каждый процесс обычно делает постоянное использование файлов.

каждый процесс состоит из дерева процессов (или таблицы процессов, в другой формулировке).

обычно операционные системы представляют каждый файл в каждом процессе by a (то есть в каждом дереве/таблице процессов).

первый файл, используемый в процессе file0, второй -file1, третий -file2 и так далее.

любое такое число является дескриптором файла.

файловые дескрипторы обычно являются целыми числами (0, 1, 2, а не 0.5, 1.5, 2.5).

учитывая, что мы часто описываем процессы как "process-tables", и учитывая, что таблицы имеют строки (записи), мы можем сказать, что ячейка файлового дескриптора в каждой записи использует для представления всей записи.

в аналогичной кстати, когда вы открываете сетевой сокет, он имеет дескриптор сокета.

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

файловые дескрипторы могут быть глобальными (процесс A начинается в 0, а заканчивается в 1 ; Процесс B начинается в 2, а заканчивается в 3) и т. д., Но, насколько я знаю, обычно в современных операционных системах файловые дескрипторы не являются глобальными и являются на самом деле процесс специфичен (процесс A начинается в 0 и заканчивается в 5, в то время как процесс B начинается в 0 и заканчивается в 10).

другие ответы добавили отличный материал. Я добавлю только мои 2 цента.

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

файловые дескрипторы привязаны к ID процесса.

мы знаем, что самые известные файловые дескрипторы-0, 1 и 2. 0 соответствует STDIN , 1 в STDOUT и 2 в STDERR .

скажем, взять shell обрабатывает в качестве примера и как он применяется для него?

проверьте этот код

мы создали процесс с идентификатором 14726 (PID). С помощью lsof -p 14726 мы можем получить такие вещи:

4-й столбец FD и самый Следующий тип столбца соответствуют файловому дескриптору и типу файлового дескриптора.

некоторые из значений для FD могут быть:

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

символ после числа i.e "1u", представляет режим, в котором файл открывается. r для чтения, w для записи, u для чтения и записи.

TYPE указывает тип файла. Некоторые из значений типов:

но все файловые дескрипторы Chr-символьный специальный файл (или файл символьного устройства)

теперь мы можем определить дескрипторов STDIN , STDOUT и STDERR легко lsof -p PID , или мы можем видеть то же самое, если мы ls /proc/PID/fd .

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

fd table

вы можете спросить себя, где эти файловые дескрипторы физически и то, что хранится в /dev/pts/6 например

Ну /dev/pts/6 живет исключительно в памяти. Это не обычные файлы, но так называемый файлы символьных устройств. Вы можете проверить это с: ls -l /dev/pts/6 и они начнут с c в моем случае crw--w---- .

просто вспомнить большинство Linux, как OS определить семь типов файлов:

  • обычные файлы
  • каталоги
  • файлы символьных устройств
  • блокировать файлы устройств
  • локальные сокеты
  • именованные трубы (FIFOs) и
  • символические ссылки

файловые дескрипторы (FD) :

  • на Linux / Unix, все это файл. Обычный файл, каталоги, и даже устройства-это файлы. Каждый файл имеет связанный номер, называемый файловым дескриптором (FD).
  • на экране также есть файловый дескриптор. При выполнении программы вывод отправляется в файловый дескриптор экрана, и вы видите вывод программы на монитор. Если вывод направляется в файл Дескриптор принтера, выход программы был бы напечатанный.

  1. стандартный ввод
  2. стандартный вывод
  3. Стандартная ошибка.

дескриптор файла для стандартной ошибки 2.
Если нет никакого каталога с именем mydir, то вывод команды будет сохранен в файл errorfile.txt
Используя "2>", мы перенаправляем вывод ошибки в файл с именем " errorfile.txt"
Таким образом, вывод программы не загроможден ошибками.

надеюсь, вы получили ответ.

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

  • на ядро все открытые файлы ссылаются файловыми дескрипторами.
  • файловый дескриптор-это неотрицательное целое число.
  • когда мы открываем существующий или создаем новый файл, ядро возвращает дескриптор файла процессу.
  • когда мы хотим прочитать или записать файл, мы идентифицируем файл с файловым дескриптором, который был перенастроен open или create, в качестве аргумента для чтения или записи.
  • каждый процесс UNIX имеет 20 дескрипторов файлов и его удаление, пронумерованные от 0 до 19, но он был расширен до 63 многими системами.
  • Первые три уже открыты, когда процесс начинается 0: стандартный ввод 1: стандартный вывод 2: выход стандартной ошибки
  • когда родительский процесс порождает процесс, дочерний процесс наследует файловые дескрипторы родителя

файловые дескрипторы-это дескрипторы файла. Они дают ссылки на файл. С их помощью мы можем читать, писать и открывать файлы.

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

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

Содержание

Стандарт 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 дескрипторов файлов. Таблица дескрипторов файлов имеет следующую структуру:

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

Содержит записи с информацией обо всех открытых файлах. В записи этой таблицы хранится текущее смещение указателя в файле, которое используется во всех операциях чтения и записи в файл, а также режим открытия файла (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

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

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

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

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

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

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

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

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

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

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

Файловый дескриптор — это неотрицательное число, которое является идентификатором потока ввода-вывода. Дескриптор может быть связан с файлом, каталогом, сокетом.

Например, когда вы открываете или создаете новый файл, операционная система формирует для себя запись для представления этого файла и хранения информации о нем. У каждого файла индивидуальный файловый дескриптор Linux. Открыли 100 файлов — где-то в ядре появились 100 записей, представленных целыми числами.

Как файлы получают дескрипторы

Обычно файловые дескрипторы выделяются последовательно. Есть пул свободных номеров. Когда вы создаете новый файл или открываете существующий, ему присваивается номер. Следующий файл получает очередной номер — например, 101, 102, 103 и так далее.

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

Дескриптор для каждого процесса является уникальным. Но есть три жестко закрепленных индекса — это первые три номера (0, 1, 2).

Номер файлового дескриптора и режим

Когда вы завершаете работу с файлом, присвоенный ему дескриптор освобождается и возвращается в пул свободных номеров. Он снова доступен для выделения под новый файл.

В Unix-подобных системах файловые дескрипторы могут относиться к любому типу файлов Unix: обычным файлам, каталогам, блочным и символьным устройствам, сокетам домена, именованным каналам. Дескрипторы также могут относиться к объектам, которые не существуют в файловой системе: анонимным каналам и сетевым сокетам.

Понятием «файловый дескриптор» оперируют и в языках программирования. Например, в Python функция os.open(path, flags, mode=0o777, *, dir_fd=None) открывает путь к файлу path, добавляет флаги и режим, а также возвращает дескриптор для вновь открытого файла. Начиная с версии 3.4 файловые дескрипторы в дочернем процессе Python не наследуются. В Unix они закрываются в дочерних процессах при выполнении новой программы.

Для чего нужны файловые дескрипторы

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

  1. В традиционной реализации Unix дескрипторы индексируются в таблицу дескрипторов для каждого процесса, поддерживаемого ядром.
  2. Таблица файловых дескрипторов индексирует общесистемную таблицу файлов, открытых всеми процессами.
  3. В таблице файлов записывается режим, в котором открыт файл или другой ресурс — например, для чтения, записи, чтения и записи.
  4. Режим индексируется в таблицу индексных дескрипторов, описывающих фактические базовые файлы. В каждом индексном дескрипторе хранятся атрибуты и расположение дисковых блоков переданного объекта.

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

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

В однопоточных приложениях такая проблема обычно не возникает.

Что можно делать с файловыми дескрипторами

Файловые дескрипторы можно использовать для исправления ошибок. Например, если на диске нет свободного места, но вы не видите файлы, которые занимают пространство, то можно посмотреть открытые дескрипторы. Это поможет понять, какое приложение заняло весь доступный объем.

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

Однажды, на одном интервью меня спросили, что ты будешь делать, если обнаружишь неработающий сервис из-за того, что на диске закончилось место?

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

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

Интервьюер прервал меня на последнем слове, дополнив свой вопрос: «Предположим, что данные нам не нужны, это просто дебаг лог, но приложение не работает из-за того, что не может записать дебаг»?

«окей», — ответил я, «мы можем выключить дебаг в конфиге приложения и перезапустить его».
Интервьюер возразил: «Нет, приложение мы перезапустить не можем, у нас в памяти все еще хранятся важные данные, а к самому сервису подключены важные клиенты, которых мы не можем заставлять переподключаться заново».

«ну хорошо», сказал я, «если мы не можем перезапускать приложение и данные нам не важны, то мы можем просто очистить этот открытый файл через файл дескриптор, даже если мы его не видим в команде ls на файловой системе».

Интервьюер остался доволен, а я нет.

Тогда я подумал, почему человек, проверяющий мои знания, не копает глубже? А что, если данные все-таки важны? Что если мы не можем перезапускать процесс, и при этом этот процесс пишет на файловую систему в раздел, на котором нет свободного места? Что если мы не можем потерять не только уже записанные данные, но и те данные, что этот процесс пишет или пытается записать?

Тузик

В начале моей карьеры я пытался создать небольшое приложение, в котором нужно было хранить информацию о пользователях. И тогда я думал, а как мне сопоставить пользователя к его данным. Есть, например, у меня Иванов Иван Иваныч, и есть у него какие-то данные, но как их подружить? Я могу указать напрямую, что собака по имени «Тузик» принадлежит этому самому Ивану. Но что, если он сменит имя и вместо Ивана станет, например, Олей? Тогда получится, что наша Оля Ивановна Иванова больше не будет иметь собаки, а наш Тузик все еще будет принадлежать несуществующему Ивану. Решить эту проблему помогла база данных, которая каждому пользователю давала уникальный идентификатор (ID), и мой Тузик привязывался к этому ID, который, по сути, был просто порядковым номером. Таким образом хозяин у тузика был с ID под номером 2, и на какой-то момент времени под этим ID был Иван, а потом под этим же ID стала Оля. Проблема человечества и животноводства была практически решена.

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

Проблема файла и программы, работающей с этим файлом, примерно такая же как нашей собаки и человека. Предположим я открыл файл под именем ivan.txt и начал в него записывать слово tuzik, но успел записать только первую букву «t» в файл, и этот файл был кем-то переименован, например в olya.txt. Но файл остался тем же самым, и я все еще хочу записать в него своего тузика. Каждый раз при открытии файла системным вызовом open в любом языке программирования я получаю уникальный ID, который указывает мне на файл, этот ID и есть файл дескриптор. И совершенно не важно, что и кто делает с этим файлом дальше, его могут удалить, его могут переименовать, ему могут поменять владельца или забрать права на чтение и запись, я все равно буду иметь к нему доступ, потому что на момент открытия файла у меня были права для его чтения и/или записи и я успел начать с ним работать, а значит должен продолжать это делать.

В Linux библиотека libc открывает для каждого запущенного приложения(процесса) 3 файл дескриптора, с номерами 0,1,2. Больше информации вы можете найти по ссылкам man stdio и man stdout

  • Файл дескриптор 0 называется STDIN и ассоциируется с вводом данных у приложения
  • Файл дескриптор 1 называется STDOUT и используется приложениями для вывода данных, например командами print
  • Файл дескриптор 2 называется STDERR и используется приложениями для вывода данных, сообщающих об ошибке

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

Например, откроем консоль с bash и посмотрим PID нашего процесса


Во второй консоли запустим


Файл дескриптор с номером 255 можете смело игнорировать в рамках данной статьи, он был открыт для своих нужд уже самим bash, а не прилинкованной библиотекой.

Сейчас все 3 файл дескриптора связаны с устройством псевдотерминала /dev/pts, но мы все равно можем ими манипулировать, например запустим во второй консоли


И в первой консоли мы увидим

Redirect и Pipe

Вы можете легко переопределить эти 3 файл дескриптора в любом процессе, в том числе и в bash, например через трубу(pipe), соединяющую два процесса, смотрим


Вы можете сами запустить эту команду с strace -f и увидеть, что происходит внутри, но я вкратце расскажу.

Наш родительский процесс bash с PID 15771 парсит нашу команду и понимает сколько именно команд мы хотим запустить, в нашем случае их две: cat и sleep. Bash знает что ему нужно создать два дочерних процесса, и объединить их одной трубой. Итого bash потребуется 2 дочерних процесса и один pipe.

Перед созданием дочерних процессов bash запускает системный вызов pipe и получает новые файл дескрипторы на временный буфер pipe, но этот буфер никак пока не связывает наши два дочерних процесса.

Для родительского процесса это выглядит так будто pipe уже есть, а дочерних процессов еще нет:


Затем с помощью системного вызова clone bash создает два дочерних процесса, и наши три процесса будут выглядеть так:


Не забываем, что clone клонирует процесс вместе со всеми файл дескрипторами, поэтому в родительском процессе и в дочерних они будут одинаковые. Задача родительского процесса с PID 15771 следить за дочерними процессами, поэтому он просто ждет ответ от дочерних.

Следовательно pipe ему не нужен, и он закрывает файл дескрипторы с номерами 3 и 4.

В первом дочернем процессе bash с PID 9004, системным вызовом dup2, меняет наш STDOUT файл дескриптор с номером 1 на файл дескриптор указывающий на pipe, в нашем случае это номер 3. Таким образом все, что первый дочерний процесс с PID 9004 будет писать в STDOUT, будет автоматически попадать в буфер pipe.

Во втором дочернем процессе с PID 9005 bash меняет с помощью dup2 файл дескриптор STDIN с номером 0. Теперь все, что будет читать наш второй bash с PID 9005, будет читать из pipe.

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

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

Далее в первом дочернем процессе с PID 9004 bash запускает с помощью системного вызова exec исполняемый файл, который мы указали в командной строке, в нашем случае это /usr/bin/cat.

Во втором дочернем процессе с PID 9005 bash запускает второй исполняемый файл, который мы указали, в нашем случае это /usr/bin/sleep.

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

Проверяем в консоли:


Как видите уникальный номер нашего pipe у нас в обоих процессах совпадает. Таким образом у нас есть связь между двумя разными процессами с одним родителем.

Для тех, кто не знаком с системными вызовами, которые использует bash, крайне рекомендую запустить команды через strace и посмотреть, что происходит внутри, например, так:


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


Запустим программу и посмотрим на файл дескрипторы


Как видим у нас есть наши 3 стандартные файл дескрипторы и еще один, который мы открыли. Проверим размер файла:


данные пишутся, пробуем поменять права на файл:


Видим, что данные все еще пишутся, хотя наш пользователь не имеет права писать в файл. Попробуем его удалить:


Куда пишутся данные? И пишутся ли вообще? Проверяем:


Да, наш файл дескриптор все еще существует, и мы можем работать с этим файл дескриптором как с нашим старым файлом, мы можем его читать, очищать и копировать.

Смотрим на размер файла:


Размер файла 19923457. Пробуем очистить файл:


Как видим размер файла только увеличивается и наш транкейт не сработал. Обратимся к документации по системному вызову open. Если при открытии файла мы используем флаг O_APPEND, то при каждой записи операционная система проверяет размер файла и пишет данные в самый конец файла, причем делает это атомарно. Это позволяет нескольким тредам или процессам писать в один и тот же файл. Но в нашем коде мы не используем этот флаг. Мы можем увидеть другой размер файла в lsof после транкейт только если откроем файл для дозаписи, а значит в нашем коде вместо


мы должны поставить


Проверяем с «w» флагом

Программируем уже запущенный процесс

Часто программисты при создании и тестировании программы используют дебагеры (например GDB) или различные уровни логирования в приложении. Linux предоставляет возможность фактически писать и менять уже запущенную программу, например менять значения переменных, устанавливать breakpoint и тд и тп.

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

Создадим файл для нашего раздела, который мы подмонтируем как отдельный диск:


Создадим файловую систему:


Подмонтируем файловую систему:


Создаем директорию с нашим владельцем:


Откроем файл только на запись в нашей программе:


Ждем несколько секунд


Итак, мы получили проблему, описанную в начале этой статьи. Свободного места 0, занятого 100%.

Мы помним, что по условиям задачи мы пытаемся записать очень важные данные, которые нельзя потерять. И при этом нам нужно починить сервис без перезапуска процесса.

Допустим, у нас все же есть место на диске, но в другом разделе, например в /home.

Попробуем «перепрограммировать на лету» наш код.

Смотрим PID нашего процесса, который съел все место на диске:


Подключаемся к процессу через gdb


Смотрим открытые файл дескрипторы:


Смотрим информацию о файл дескрипторе с номером 3, который нас интересует


Помня о том, какой системный вызов делает Python (смотрите выше, где мы запускали strace и находили вызов open), обрабатывая наш код для открытия файла, мы делаем то же самое самостоятельно от имени нашего процесса, но биты O_WRONLY|O_CREAT|O_TRUNC нам нужно заменить на числовое значение. Для этого открываем исходники ядра, например тут и смотрим какие флаги за что отвечают

Объединяем все значения в одно, получаем 00001101

Запускаем наш вызов из gdb


Итак мы получили новый файл дескриптор с номером 4 и новый открытый файл на другом разделе, проверяем:


Мы помним пример с pipe — как bash меняет файл дескрипторы, и уже выучили системный вызов dup2.

Пробуем подменить один файл дескриптор другим


Закрываем файл дескриптор 4, так как нам он не нужен:


И выходим из gdb


Проверяем новый файл:


Как видим, данные пишутся в новый файл, проверяем старый:


Данные не потеряны, приложение работает, логи пишутся в новое место.

Немного усложним задачу

Представим, что данные нам важны, но места на диске у нас нет ни в одном из разделов и подключить диск мы не можем.

Перезапускаем приложение, и проверяем:


Места на диске нет, но мы успешно создаем там именованный pipe:


Теперь нам надо как-то завернуть все данные, что попадают в этот pipe на другой сервер через сеть, для этого подойдет все тот же netcat.


На нашем проблемном сервере запускаем в отдельном терминале


Теперь все данные, которые попадут в pipe автоматически попадут на stdin в netcat, который их отправит в сеть на порт 7777.

Все что нам осталось сделать это начать писать наши данные в этот именованный pipe.

У нас уже есть запущенное приложение:


Из всех флагов нам нужен только O_WRONLY так как файл уже существует и очищать нам его не нужно


Данные идут, проверяем проблемный сервер


Данные сохранились, проблема решена.

Пользуясь случаем, передаю привет коллегам из компании Degiro.
Слушайте подкасты Радио-Т.

В качестве домашнего задания предлагаю подумать, что будет в файл дескрипторах процесса cat и sleep если запустить такую команду:

Что может быть более упрощенным описанием файловых дескрипторов по сравнению с Википедией? Зачем они нужны? Скажем, возьмем процессы оболочки в качестве примера и как это применимо к нему?

Содержит ли таблица процессов более одного дескриптора файла. Если да, то почему?

А как насчет концепции stdin stdout stderr и т. Д.? У меня есть экземпляр, например, сказать, что процесс браузера открыт, и он открыл некоторые временные файлы для отображения моего HTML. Процесс использует тот же FD для чтения / записи? Кроме того, таблица процессов . имеет записи, такие как указатель fd0 указатель fd1 указатель fd2 . означает ли это, что все эти файлы находятся в оперативной памяти? Почему еще указатели? Когда вы открываете файл, ОС создает поток для этого файла и подключает этот поток к открытому файлу, дескриптор фактически представляет этот поток. Точно так же есть некоторые потоки по умолчанию, созданные ОС. Эти потоки подключены к вашему терминалу, а не к файлам. Поэтому, когда вы пишете что-то в терминале, оно идет в поток stdin и ОС. И когда вы пишете команду «ls» на терминале, ОС записывает вывод в поток stdout. Поток stdout подключен к терминалу вашего монитора, поэтому вы можете видеть выходной сигнал. Что касается примера браузера, необязательно, чтобы браузер сохранял файлы открытыми. Это зависит от реализации браузера, но в большинстве случаев браузер открывает временный файл, записывает файл и закрывает файл, поэтому нет необходимости открывать файл, даже если веб-страница открыта. А дескриптор просто хранит информацию о файле и не обязательно хранит файл в оперативной памяти. Когда вы читаете данные из дескриптора, ОС считывает данные с жесткого диска. Информация в дескрипторе файла просто представляет местоположение файла на жестком диске и т.д .. Файловый дескриптор для файла не является однозначным отображением. Я мог открыть () один и тот же файл 4 раза и получить 4 разных файловых дескриптора. Каждый из которых может быть использован (в зависимости от флагов, передаваемых open ()) для чтения, записи или для того и другого. Насколько файл находится в оперативной памяти или на диске - это скрыто от вас ядром и его различными кешами. В конечном итоге, что такое кеш, будет соответствовать тому, что находится на диске (для записи), и ядро ​​не вернется на диск для чтения, если данные уже находятся в кеше.

Проще говоря, когда вы открываете файл, операционная система создает запись для представления этого файла и сохранения информации об этом открытом файле. Таким образом, если в вашей ОС открыто 100 файлов, то в ОС будет 100 записей (где-то в ядре). Эти записи представлены целыми числами, такими как (. 100, 101, 102 . ). Этот номер записи является дескриптором файла. Так что это просто целое число, которое уникально представляет открытый файл в операционной системе. Если ваш процесс открывает 10 файлов, то ваша таблица процессов будет иметь 10 записей для файловых дескрипторов.

Аналогично, когда вы открываете сетевой сокет, он также представляется целым числом и называется дескриптором сокета. Я надеюсь, вы понимаете.

Кроме того, именно поэтому вы можете исчерпать файловые дескрипторы, если открываете много файлов одновременно. Что помешает запуску систем * nix, так как они постоянно открывают дескрипторы для заполнения /proc . @ErbenMo: Нет, это не может быть так же. Когда вы открываете файл, операционная система назначает доступный FD, а когда вы закрываете его, ОС освобождает FD и может назначить этот FD другому файлу, открытому после этого. Операционная система отслеживает открытые файлы и не имеет ничего общего с конкретным файлом. « Так что это просто целое число, которое уникально представляет открытый файл в операционной системе. » Это неверно. Это целое число уникально представляет открытый файл в процессе . Например, дескриптор файла 0 будет представлять один открытый файл в одном процессе и совершенно другой открытый файл в другом процессе. @Tayyab: я считаю, что вы ошибаетесь. Файловые дескрипторы 0, 1 и 2 являются стандартным вводом, стандартным выводом и стандартной ошибкой для каждого запущенного процесса. Успешный начальный вызов open() даст вам файловый дескриптор 3, даже если другой запущенный процесс имеет файловый дескриптор 3. См. Определение POSIX open() : «Функция open () должна вернуть файловый дескриптор для именованного файла, который является самым низким дескриптор файла в данный момент не открыт для этого процесса . " (выделение добавлено). @KeithThompson: Да, вы правы. На самом деле это об уровне абстракции. На самом деле поддерживаются две таблицы, первая из которых предназначена для каждого процесса, а вторая - для всей системы. FD в таблице процессов (т. Е. Fdtable) не является уникальной для всей системы. Однако он сопоставляется с таблицей v-узлов, которая содержит уникальные записи всей системы. Таким образом, когда вы вызываете функции fopen () и fileno () для проверки дескриптора, вы можете получить одно и то же число FD в 2 разных процессах, потому что он возвращает индекс fdtable для каждого процесса. Спасибо, что подняли это!

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

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

Я лично считаю, что этот ответ лучше, чем тот, который помечен как ответ. Upvoted.

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

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

Два процесса

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

Поскольку 0, 1, 2 связаны с «stdin», «stdout» и «stderr» процесса, можем ли мы использовать эти дескрипторы одновременно для разных процессов? @Tarik: файловые дескрипторы для каждого процесса. Чтобы увидеть это, загрузите osquery и выполните osqueryi <<< echo '.all process_open_files' в оболочке bash.

Другие ответы добавили отличные вещи. Я добавлю только мои 2 цента.

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

Файловые дескрипторы привязаны к идентификатору процесса.

Мы знаем, что наиболее известные файловые дескрипторы - это 0, 1 и 2. 0 соответствует STDIN , 1 к STDOUT и 2 к STDERR .

Скажем, возьмем процессы оболочки в качестве примера и как это применимо к нему?

Проверьте этот код

Мы создали процесс с идентификатором 14726 (PID). Используя lsof -p 14726 мы можем получить такие вещи:

Четвертый столбец FD и следующий столбец TYPE соответствуют дескриптору файла и типу дескриптора файла.

Некоторые значения для FD могут быть:

Но настоящий дескриптор файла находится под:

Символ после числа, т.е. «1u», представляет режим, в котором файл открывается. r для чтения, w для записи, u для чтения и записи.

TYPE указывает тип файла. Некоторые из значений типов:

Но все файловые дескрипторы являются CHR - символьный специальный файл (или файл символьного устройства)

Теперь мы можем определить дескрипторы файлов для STDIN , STDOUT и STDERR легко с lsof -p PID , или мы можем увидеть то же самое , если мы ls /proc/PID/fd .

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

таблица ФД

Вы можете спросить себя, где эти файловые дескрипторы физически и что хранится, /dev/pts/6 например, в

Ну, /dev/pts/6 живет чисто в памяти. Это не обычные файлы, а так называемые файлы символьных устройств . Вы можете проверить это с помощью: ls -l /dev/pts/6 и они начнутся с c , в моем случае crw--w---- .

Напомним, что большинство Linux-подобных ОС определяют семь типов файлов:

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