Что происходит, когда я закрываю () файловый дескриптор?

16

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

 _process1_
|          |
| 0 stdin  |
| 1 stdout |
| 2 stderr |
|__________|

Затем я закрываю файловый дескриптор 1:

close(1);

Файловый дескриптор 1 переводит (указывает) в структуру stdout FILE в таблице открытых файлов ядра .

С кодом выше дескриптор файла 1 удаляется из таблицы процесса, которая становится:

 _process1_
|          |
| 0 stdin  |
| 2 stderr |
|__________|

Но что происходит в ядре? Имеет ли stdoutполучить освобождаться структуру FILE? Как это возможно, если стандартный вывод является специальным файлом (монитором) и, вероятно, используется другими процессами? Как насчет структур FILE, которые являются обычными файлами (например, .txt)? Что, если такой файл используется другим процессом?

Pithikos
источник

Ответы:

13

Файловый дескриптор 1 преобразуется в структуру stdout FILE в таблице открытых файлов ядра.

Это недоразумение. Файловая таблица ядра не имеет ничего общего с файловыми структурами пространства пользователя.

В любом случае ядро ​​имеет два уровня косвенности. Существует внутренняя структура, представляющая сам файл, который считается подсчитанным. Существует «открытое описание файла», которое подсчитано. И затем есть дескриптор файла, который не считается ссылкой. Структура файла указывает путь к самому inode. Описание открытого файла содержит такие вещи, как режим открытия и указатель файла.

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

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

Дэвид Шварц
источник
У меня небольшие затруднения с пониманием терминологии в вашем ответе. Я предполагаю, что указатель файла означает «смещение файла». Это то, что вы имели в виду? И что вы имели ввиду под дескриптором файла ?
Компьютерщик
Это верно, под «смещением файла» я имею в виду смещение, при котором будет происходить последующее чтение или запись. «Дескриптор файла» - это связь между процессом и описанием открытого файла - это то, что вы получите, когда openпреуспеете.
Дэвид Шварц
6

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

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

Когда все ссылки на файл (записи каталога и дескрипторы файлов) будут удалены, код файловой системы помечает индекс для повторного использования. Любые блоки в файле становятся доступными для размещения. Многие файловые системы очищают указатели блоков в inode после его освобождения. Это затрудняет восстановление удаленного файла. Обновления на диске могут быть буферизованы и завершены позднее.

BillThor
источник
1
Два вопроса: (1) действительно ли пересчитаны файловые дескрипторы? Когда вы контролируете -d a cat > some.file, cat получает EOF на stdin, а оболочка - нет. (2) Почему подсчет ссылок? Почему не какая-то форма сборки мусора? Разве GC не намного лучше в пространстве пользователя?
Брюс Эдигер
Расширение ответа BillThor: в обычных случаях stdin, stdout и stderr - это просто дескрипторы файлов на устройстве TTY. Таким образом, если вы закроете дескриптор файла, это устройство TTY все еще там, и его можно будет снова открыть позже.
Патрик
1
@BruceEdiger: (1) когда оболочка запускает cat > some.fileто, что она делает, она разветвляется, открывает файл some.file и присваивает его дескриптору файла 1, затем она делает это exec("cat"). Когда процесс exec () 'd, он наследует дескрипторы открытого файла.
Патрик
@BruceEdiger (2) Подсчет ссылок является идеальной формой сборки мусора, когда он используется для структур данных, которые не содержат указателей (или цепочек указателей, заканчивающихся на) других структур данных того же типа. Кроме того, это происходит в пространстве ядра (не то, чтобы это имело большое значение).
Жиль "ТАК - перестать быть злым"