Что такое каталоги, если все в Linux является файлом?

17

Очень часто начинающие слышат фразу «Все это файл в Linux / Unix». Тем не менее, какие каталоги тогда? Чем они отличаются от файлов?

Сергей Колодяжный
источник

Ответы:

22

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

Понимание файловой системы и файлов Unix / Linux: все является индексом

По сути, каталог - это просто специальный файл, который содержит список записей и их 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-Block

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

Дерево каталогов

Файловые системы реализуют дерево каталогов как одну из структур данных дерева. Особенно,

  • ext3 и ext4 используют HTree
  • XFS использует B + Tree
  • ZFS использует хэш-дерево

Ключевым моментом здесь является то, что сами каталоги являются узлами в дереве, а подкаталоги являются дочерними узлами, причем каждый дочерний узел имеет ссылку на родительский узел. Таким образом, для ссылки на каталог число инодов составляет минимум 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поле.

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

Сергей Колодяжный
источник
2
Я отказываюсь принимать сокеты - файлы;) Будет ли "все доступно как файл" быть более точным?
Rinzwind
@Rinzwind Ну, фраза «все доступно в виде файла» является точной. У обычных файлов есть open()и read()сокеты, connect()и read()тоже. Что было бы более точным, так это то, что «файл» - это действительно организованные «данные», хранящиеся на диске или в памяти, а некоторые файлы являются анонимными - у них нет имени файла. Обычно пользователи думают о файлах в виде этого значка на рабочем столе, но это не единственное, что существует. См. Также unix.stackexchange.com/a/116616/85039
Сергей Колодяжный
Ну, вопрос был больше о том, был ли каталог файлом. И это. Сокеты могут быть почти отдельным вопросом вместе с именованными каналами FIFO.
WinEunuuchs2Unix
Ну, я до сих пор получил ответ о трубах: askubuntu.com/a/1074550/295286 Может быть, FIFO будут следующими
Сергей Колодяжный,