Очень часто начинающие слышат фразу «Все это файл в Linux / Unix». Тем не менее, какие каталоги тогда? Чем они отличаются от файлов?
Очень часто начинающие слышат фразу «Все это файл в Linux / Unix». Тем не менее, какие каталоги тогда? Чем они отличаются от файлов?
Ответы:
Примечание. Первоначально это было написано для поддержки моего ответа на вопрос. Почему текущий каталог в ls
команде идентифицирован как связанный с самим собой? но я чувствовал, что это тема, которая заслуживает того, чтобы стоять самостоятельно, и, следовательно, это вопросы и ответы .
По сути, каталог - это просто специальный файл, который содержит список записей и их ID.
Прежде чем мы начнем обсуждение, важно провести различие между несколькими терминами и понять, что на самом деле представляют каталоги и файлы. Возможно, вы слышали выражение «Все это файл» для Unix / Linux. Хорошо, что пользователи часто понимают под файлом: /etc/passwd
- Объект с путем и именем. На самом деле имя (будь то каталог, файл или что-то еще) - это просто текстовая строка - свойство реального объекта. Этот объект называется inode или I-номером и хранится на диске в таблице inode. Открытые программы также имеют таблицы инодов, но сейчас это не наше дело.
Представление Unix о каталоге таково, как Кен Томпсон изложил его в интервью 1989 года :
... А потом некоторые из этих файлов были каталогами, в которых были только имя и I-номер.
Интересное наблюдение можно сделать из выступления Денниса Ричи в 1972 году о том, что
«... каталог на самом деле не более чем файл, но его содержимое контролируется системой, а содержимое - это имена других файлов. (В других системах каталог иногда называют каталогом.)»
... но нигде в разговоре нет упоминаний об инодах. Тем не менее, руководство 1971 года о format of directories
состоянии:
Тот факт, что файл является каталогом, указывается битом в слове флага его записи i-узла.
Записи каталога имеют длину 10 байт. Первое слово - это i-узел файла, представленный записью, если не ноль; если ноль, запись пуста.
Так было с самого начала.
Спаривание каталогов и inode также объясняется в разделе Как структуры каталогов хранятся в файловой системе UNIX? , Сам каталог является структурой данных, точнее: списком объектов (файлов и номеров узлов), указывающих на списки этих объектов (разрешения, тип, владелец, размер и т. д.). Таким образом, каждый каталог содержит свой собственный номер inode, а затем имена файлов и их номера inode. Самым известным является индекс № 2, который является /
каталогом . (Обратите внимание, что хотя они /dev
и /run
являются виртуальными файловыми системами, поэтому, поскольку они являются корневыми папками для своей файловой системы, они также имеют индекс 2; т. е. индекс уникален в своей файловой системе, но с несколькими подключенными файловыми системами у вас есть неуникальные индексы). диаграмма, заимствованная из связанного вопроса, вероятно, объясняет это более кратко:
Вся эта информация, хранящаяся в inode, может быть доступна через stat()
системные вызовы, как в Linux man 7 inode
:
Каждый файл имеет индекс, содержащий метаданные о файле. Приложение может извлечь эти метаданные, используя stat (2) (или связанные вызовы), который возвращает структуру stat, или statx (2), который возвращает структуру statx.
Можно ли получить доступ к файлу, только зная его номер инода ( ref1 , ref2 )? В некоторых реализациях Unix это возможно, но оно обходит проверки прав и доступа, поэтому в Linux это не реализовано, и вам нужно пройти по дереву файловой системы ( find <DIR> -inum 1234
например, через ), чтобы получить имя файла и соответствующий ему inode.
На уровне исходного кода он определен в исходном коде ядра Linux и также используется многими файловыми системами, работающими в операционных системах Unix / Linux, включая файловые системы ext3 и ext4 (по умолчанию Ubuntu). Интересная вещь: с данными, являющимися просто блоками информации, Linux фактически имеет функцию inode_init_always, которая может определить, является ли inode pipe ( inode->i_pipe
). Да, сокеты и каналы технически также являются файлами - анонимными файлами, которые могут не иметь имени файла на диске. Сокеты FIFO и Unix-Domain имеют имена файлов в файловой системе.
Сами данные могут быть уникальными, но номера инодов не уникальны. Если у нас есть жесткая ссылка на foo, называемую foobar, это также будет указывать на индекс 123. Этот инод сам содержит информацию о том, какие фактические блоки дискового пространства заняты этим инодом. И это технически, как вы можете .
быть связаны с именем файла каталога. Ну, почти: вы не можете создавать жесткие ссылки на каталоги в Linux самостоятельно , но файловые системы могут разрешать жесткие ссылки на каталоги очень дисциплинированным образом, что ограничивает наличие только .
и ..
как жестких ссылок.
Файловые системы реализуют дерево каталогов как одну из структур данных дерева. Особенно,
Ключевым моментом здесь является то, что сами каталоги являются узлами в дереве, а подкаталоги являются дочерними узлами, причем каждый дочерний узел имеет ссылку на родительский узел. Таким образом, для ссылки на каталог число инодов составляет минимум 2 для чистого каталога (ссылка на имя каталога /home/example/
и ссылка на себя /home/example/.
), а каждый дополнительный подкаталог является дополнительной ссылкой / узлом:
# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24
Диаграмма, найденная на странице курса Яна Д. Аллена, показывает упрощенную очень ясную диаграмму:
WRONG - names on things RIGHT - names above things
======================= ==========================
R O O T ---> [etc,bin,home] <-- ROOT directory
/ | \ / | \
etc bin home ---> [passwd] [ls,rm] [abcd0001]
| / \ \ | / \ |
| ls rm abcd0001 ---> | <data> <data> [.bashrc]
| | | |
passwd .bashrc ---> <data> <data>
Единственное, что неправильно в ПРАВИЛЬНОЙ диаграмме, это то, что технически файлы не считаются находящимися в самом дереве каталогов: добавление файла не влияет на количество ссылок:
$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4
Цитировать Линуса Торвальдса :
Весь смысл «все это файл» не в том, что у вас есть какое-то случайное имя файла (действительно, сокеты и каналы показывают, что «файл» и «имя файла» не имеют ничего общего друг с другом), а в том, что вы можете использовать общее инструменты для работы на разные вещи.
Учитывая, что каталог - это особый случай файла, естественно, должны существовать API, которые позволяют нам открывать / читать / записывать / закрывать их аналогично обычным файлам.
Вот тут-то и появляется dirent.h
библиотека C, которая определяет dirent
структуру, которую вы можете найти в man 3 readdir :
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
Таким образом, в вашем C-коде вы должны определить struct dirent *entry_p
, и когда мы откроем каталог с opendir()
и начнем читать его readdir()
, мы будем хранить каждый элемент в этой entry_p
структуре. Конечно, каждый элемент будет содержать поля, определенные в шаблоне для dirent
показанного выше.
Практический пример того, как это работает, можно найти в моем ответе « Как составить список файлов и номеров их узлов в текущем рабочем каталоге» .
Обратите внимание , что руководство POSIX на fdopen гласит , что «[т] запись каталога для точки и точка-точка является необязательной» и READDIR руководства заявляет struct dirent
, только необходимо иметь d_name
и d_ino
поле.
Примечание о «записи» в каталоги: запись в каталог изменяет его «список» записей. Следовательно, создание или удаление файла напрямую связано с разрешениями на запись в каталог , а добавление / удаление файлов является операцией записи в указанном каталоге.
open()
и read()
сокеты, connect()
и read()
тоже. Что было бы более точным, так это то, что «файл» - это действительно организованные «данные», хранящиеся на диске или в памяти, а некоторые файлы являются анонимными - у них нет имени файла. Обычно пользователи думают о файлах в виде этого значка на рабочем столе, но это не единственное, что существует. См. Также unix.stackexchange.com/a/116616/85039