Что такое ментальная модель?

Моя единственная цель — описать, как я представляю себе концепцию, когда думаю о ней.

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

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

Другими словами, цель состоит в том, чтобы, когда вы думаете о каком-либо понятии, его мысленная модель помогала вам взглянуть на него под правильным углом. Верно, вот как это работает, а теперь давайте посмотрим в инструкции, как это можно использовать.

Меня всегда беспокоило мое поверхностное понимание индексных дескрипторов, жестких и мягких ссылок в Linux. Здесь я пытаюсь структурировать свои знания в упрощенную ментальную модель.

Что составляет файл в Linux?

На уровне хранения файл — блок данных. Это не совсем так, но не буду углубляться в детали.

На уровне файловой системы этот блок данных представлен в виде абстракции, называемой индексным дескриптором. Индексный дескриптор (inode) можно представить как структуру данных, которая хранит метаданные об основном блоке данных.

Имя пути (pathname) делает эту пару (индексный дескриптор и блок данных) доступной, полезной чеовеку.

Что за pathname?

Я хотел, чтобы комбинацию путь к файлу + имя файла обозначал термин из одного слова — pathname. Термины имя файла или путь к файлу не передавали контекст должным образом.

RFC 959 протокола передачи файлов FTP дал мне подходящий термин — имя пути. Его определение таково:

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

Представьте, что блок данных файла находится на уровне абстракции ниже, чем имя пути, которое находится на уровне файловой системы. Индексный дескриптор действует как мост — представитель блока данных на уровне файловой системы.

Как же трудно сохранить высокий уровень обзора, не вдаваясь в детали.

Компоненты файла. Сверху вниз: блок данных, индексный дескриптор и путь к файлу

Грубо говоря, файл = имя_пути + индексный дескриптор + блок данных.

Так, когда вы говорите о файле, то можете иметь в виду все эти компоненты, или любой из них.

Жесткие ссылки

Блок данных файла представлен ровно одним индексным дескриптором на уровне файловой системы. Но если рассматривать пути как метки, прикрепленные к паре (индексного дескриптора и блока данных), то на один и тот же индексный дескриптор можно ссылаться с помощью разных меток.

Два пути — /tmp/filename1 и /home/bhoot/filename2 — указывают на один и тот же индексный дескриптор, представляя собой две жесткие ссылки.

На рисунке выше /tmp/filename1 и /home/bhoot/filename2 указывают на один и тот же индекс. Это две жесткие ссылки. Жесткая ссылка присоединяет путь к индексу.

Рисунок выше можно интерпретировать по-разному:

  1. Имена путей указывают на индекс, а не наоборот. Поэтому имя пути знает об индексе, а сам индексный дескриптор об имени пути не знает.

Как хранится соединение по жесткой ссылке?

Он хранится в каталоге, содержащем имя файла. Каталог — это, по сути, «таблица», в которой каждая «строка» регистрирует файл каталога. Каждая строка состоит из пути к файлу и указателя на индексный дескриптор, на который он указывает. Таким образом, путь к файлу осведомлен об индексном дескрипторе, но не наоборот.

  1. Можно ли на этом уровне абстракции определить, какое из двух имен путей создавало файл изначально? Думаю, что нет. Считается, что жесткие ссылки равнозначны друг другу.
  2. Как следствие сказанного выше, к файлу всегда прикреплена хотя бы одна жесткая ссылка. Когда вы создаете файл, например, с помощью touch /a/pathname, то создаете жесткую ссылку между /a/pathname и индексным дескриптором файла.

Удаление жесткой ссылки

Посмотрите рисунок выше. Что должно произойти с точки зрения здравого смысла, если сделать rm /tmp/filename1?

Должна ли rm удалить всю комбинацию — /tmp/имя_файла1 + индексный дескриптор + блок данных? Тогда что произойдет с другим именем пути /home/bhoot/filename2, которое указывает на тот же индексный дескриптор?

Правильным решением было бы сохранить целостность другого пути /home/bhoot/filename2 и удалить только целевой путь /tmp/filename1.

Это то, что происходит при удалении файла — удаляется (жесткая) ссылка между индексным дескриптором и указанным именем пути.

Аналогично, команда mv f1 f2 создает новую жесткую ссылку f2 и удаляет старую — f1.

Представление одной из двух жестких ссылок, связанных с одним и тем же индексным дескриптором, удаляется. Удаляется только затронутое имя пути. Индексный дескриптор и базовый блок данных остаются нетронутыми.

Что происходит, когда удаляются все жесткие ссылки на файл? В файловой системе больше нет путей, которые ссылаются на блок данных. Поэтому индексный дескриптор помечен для удаления, а связанный блок данных — сирота.

Когда удаляются все жесткие ссылки на индексный дескриптор, из файловой системы удаляется весь файл вместе с его содержимым.

Мягкие ссылки

Взгляните на следующий рисунок.

Слева обычный файл, идентифицируемый по пути /home/bhoot/filename2. Справа — файл-ссылка, идентифицируемый по пути filename3. Файл-ссылка указывает на обычный файл. И обычный файл, и файл-ссылка имеют собственные индексные дескрипторы и собственные блоки данных.

На рисунке выше показана мягкая ссылка синего цвета (она синяя, верно?), указывающая от файла ссылки filename3 на целевой файлу /home/bhoot/filename2. Мягкая (или символическая) ссылка указывает мягкой ссылке на целевой файл.

Таким образом, мягкая ссылка связывает файл-ссылку с целевым файлом. В отличие от жесткой ссылки, которая связывает имя пути с индексным дескриптором.

На странице руководства POSIX ln синтаксис описывается так: ln [-s] source target, где источник — это файл, на который будет сделана ссылка, тогда как цель — новый файл ссылки, который будет создан. Эти обозначения могут иметь смысл в конкретном контексте создания мягкой ссылки. Но, как только мягкая ссылка уже создана, называть ее целью не имеет смысла.

Я предпочитаю термины в мане GNU ln: ln [-s] target link_name, где target — это файл, на который нужно указать, а link_name — новый файл ссылки.

Мягкая ссылка — это файл

Как мы видели в разделе «Что такое файл в Linux?», имя пути всегда связано с индексным дескриптором + блоком данных. Это верно и в случае с мягкой ссылкой. На приведенном выше рисунке файл мягкой ссылки filename3, который указывает на /home/bhoot/filename2, также имеет собственный блок данных и индексный дескриптор.

Вы можете проверить номер индексного дескриптора, связанный с именем пути, с помощью команды ls -i. Два файла жестких ссылок, указывающие на один и тот же индексный дескриптор, будут иметь одинаковый номер индексного дескриптора, а файл мягкой ссылки и его целевой файл будут иметь разные номера дескрипторов.

# создаем обычный файл
$ echo "HELLO WORLD HOW ARE YOU?" > file.txt

# создаем жесткую ссылку
$ ln file.txt hardlink.txt

# обе жесткие ссылки относятся к одному и тому же индексному дескриптору
$ ls -i file.txt hardlink.txt 
17751620 file.txt  17751620 hardlink.txt

# создаем мягкую ссылку
$ ln -s file.txt softlink.txt

# индексный дескриптор файла мягкой ссылки отличается от целевого файла
$ ls -i file.txt softlink.txt 
17751620 file.txt  17751663 softlink.txt

Содержимое файла мягкой ссылки

Как хранится связь с мягкой ссылкой? В блоке данных файла мягкой ссылки. Содержимое мягкой ссылки — путь к целевому файлу, на который она указывает.

Если файл мягкой ссылки создается в том же каталоге, что и целевой файл, то в качестве содержимого файла мягкой ссылки сохраняется только базовое имя (basename) целевого файла. Иначе сохраняется абсолютное имя целевого файла.

Косайода на lobste.rs 10 ноября 2024 г.: вежливо указал, что мое утверждение выше — чушь. После нескольких тестов я согласился.

Посмотрите полное объяснение. Вкратце оно выглядит так: ln -s some-file.txt link-file.txt просто создает файл link-file.txt, содержимое которого — some-file.txt.

Есть два способа это проверить:

  1. readlink filename3 выводит содержимое мягкой ссылки filename3, которая является именем целевого пути.
   $ readlink filename3
   /home/bhoot/filename2
  1. stat --format="%s" filename3 выводит размер filename3, равный количеству символов в имени /home/bhoot/filename2.
   $ stat --format="%s" filename3
   21

Операции ввода-вывода с файлом мягкой ссылки

Копирование файла мягкой ссылки

Что происходит при копировании файла с мягкой ссылкой? То же самое, что и при копировании обычного файла. По крайней мере, так я думал.

# создаем обычный файл
$ echo "HELLO WORLD HOW ARE YOU?" > file.txt

# проверяем размер содержимого целевого файла
$ stat --format="%s" file.txt
25

# создаем мягкую ссылку
$ ln -s file.txt softlink.txt

# проверяем размер файла мягкой ссылки
$ stat --format="%s" softlink.txt
8

# копируем файл мягкой ссылки
$ cp softlink.txt softlink2.txt

# проверяем размер нового файла мягкой ссылки, который должен быть равен размеру файла мягкой ссылки
$ stat --format="%s" softlink2.txt
25 # ЧТО?

Что произошло? Команда cp просто скопировала целевой файл вместо файла ссылки.

Предполагаю, что когда эти операции ввода-вывода, связанные с блоком данных файла, выполняются для файла мягкой ссылки, на самом деле они выполняются для целевого файла, на который указывает файл мягкой ссылки.

Таким образом, cp softlink.txt softlink2.txt фактически скопирует целевой файл. Аналогично, cat softlink.txt выведет содержимое целевого файла. Но mv не будет работать с базовым блоком данных файла — только с самим файлом мягкой ссылки.

Примечание. Когда я писал абзацы выше, целью была только удобная логика, чтобы запомнить, как операции ввода-вывода работают на мягких ссылках по умолчанию. Метод запоминания не обязательно должен быть технически точным. Однако я отказался от этих абзацев, потому что логика вопиюще неточна и справедливо была оспорена другими. По умолчанию cp работает так, как описано выше, но cp -P копирует саму мягкую ссылку, то есть индексный дескриптор и блок данных мягкой ссылки.

Перемещение файла мягкой ссылки

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

При перемещении файла мягкой ссылки старое имя пути мягкой ссылки заменяется новым. Новое имя пути продолжает указывать на тот же целевой файл.

Перемещение целевого файла

Более интересный вопрос, над которым стоит задуматься: что происходит, когда перемещается или удаляется целевой файл? Рассмотрим случай полного удаления.

Перемещение или удаление целевого файла — более деструктивная операция

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

В заключение

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

Поэтому, с точки зрения структуры существования, жесткие ссылки на самом деле выглядят мягче, чем мягкие.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Jayesh Bhoot: A mental model for Linux file, hard and soft links

Предыдущая статьяПочему имена Android-пакетов имеют вид com.xyz.abc?
Следующая статьяПочему стоит использовать Argo CD вместо (или вместе) с Helm в среде Kubernetes