Что такое каталоги, если все в 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 поля.

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

Другие вопросы по тегам