Что такое каталоги, если все в Linux является файлом?
Очень часто начинающие слышат фразу "Все это файл в Linux/Unix". Тем не менее, какие каталоги тогда? Чем они отличаются от файлов?
1 ответ
Примечание: изначально это было написано в поддержку моего ответа на вопрос " Почему текущий каталог находится в ls
Команда идентифицирована как связанная с самим собой? но я чувствовал, что это тема, которая заслуживает того, чтобы стоять самостоятельно, и, следовательно, это вопросы и ответы.
Понимание файловой системы и файлов Unix/Linux: все является индексом
По сути, каталог - это просто специальный файл, который содержит список записей и их ID.
Прежде чем мы начнем обсуждение, важно провести различие между несколькими терминами и понять, что на самом деле представляют каталоги и файлы. Возможно, вы слышали выражение "Все это файл" для Unix/Linux. Хорошо, что пользователи часто понимают под файлом: /etc/passwd
- Объект с путем и именем. На самом деле имя (будь то каталог, файл или что-то еще) - это просто текстовая строка - свойство реального объекта. Этот объект называется inode или I-номером и хранится на диске в таблице inode. Открытые программы также имеют таблицы инодов, но сейчас это не наше дело.
Представление Unix о каталоге таково, как Кен Томпсон сказал в интервью 1989 года:
... А потом некоторые из этих файлов были каталогами, в которых были только имя и I-номер.
Интересное наблюдение можно сделать из выступления Денниса Ричи в 1972 году о том, что
"... каталог на самом деле не более чем файл, но его содержимое контролируется системой, а содержимое - это имена других файлов. (В других системах каталог иногда называют каталогом.)"
... но нигде в разговоре нет упоминаний об инодах. Тем не менее, руководство по 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
например), чтобы получить имя файла и соответствующий ему индекс.
На уровне исходного кода он определен в исходном коде ядра Linux и также используется многими файловыми системами, работающими в операционных системах Unix/Linux, включая файловые системы ext3 и ext4 (по умолчанию Ubuntu). Интересная вещь: с данными, являющимися просто блоками информации, Linux фактически имеет функцию inode_init_always, которая может определить, является ли inode конвейером (inode->i_pipe
). Да, сокеты и каналы технически также являются файлами - анонимными файлами, которые могут не иметь имени файла на диске. Сокеты FIFO и Unix-Domain имеют имена файлов в файловой системе.
Сами данные могут быть уникальными, но номера инодов не уникальны. Если у нас есть жесткая ссылка на foo, называемую foobar, это также будет указывать на индекс 123. Этот инод сам содержит информацию о том, какие фактические блоки дискового пространства заняты этим инодом. И это технически, как вы можете иметь .
будучи связанным с именем файла каталога. Ну, почти: вы не можете создавать жесткие ссылки на каталоги в Linux самостоятельно, но файловые системы могут разрешать жесткие ссылки на каталоги очень дисциплинированным образом, что ограничивает наличие только .
а также ..
как жесткие ссылки.
Дерево каталогов
Файловые системы реализуют дерево каталогов как одну из структур данных дерева. Особенно,
- ext3 и ext4 используют HTree
- XFS использует B+ Tree
- ZFS использует хэш-дерево
Ключевым моментом здесь является то, что сами каталоги являются узлами в дереве, а подкаталоги являются дочерними узлами, причем каждый дочерний узел имеет ссылку на родительский узел. Таким образом, для ссылки на каталог количество инодов составляет минимум 2 для пустой директории (ссылка на родителя ..
и ссылка на себя .
), и каждый дополнительный подкаталог является дополнительной ссылкой / узлом:
# 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 говорится, что "[t] каталоговые записи для точек и точек-точек являются необязательными" и ручные состояния readdir struct dirent
требуется только иметь d_name
а также d_ino
поля.
Примечание о "записи" в каталоги: запись в каталог изменяет его "список" записей. Следовательно, создание или удаление файла напрямую связано с разрешениями на запись в каталог, а добавление / удаление файлов является операцией записи в указанном каталоге.