Вопрос или проблема
Я часто ловлю себя на том, что гуглю разницу между символическими ссылками и жёсткими ссылками.
Каждый раз прихожу к выводу, что жёсткие ссылки выглядят лучше в большинстве случаев, особенно когда Linux — моя основная система.
Тем не менее, меня не удовлетворяет этот вывод, потому что почти каждое руководство, блог или ответ на Stack Overflow, обсуждающие ссылки, в подавляющем большинстве рекомендуют символические ссылки.
Моё понимание на данный момент:
В системе Unix “файл” по сути является адресом, указывающим на данные на диске.
Жёсткая ссылка позволяет нескольким адресам ссылаться на одни и те же данные, что делает её мощным инструментом.
Например, если я создаю жёсткую ссылку на файл в каталоге ~/Documents
, я могу получить доступ к тем же данным из каталога ~/Desktop
, и если я перемещу файл из ~/Documents
в ~/Images
, жёсткая ссылка продолжит работать без проблем.
Такое поведение напоминает мне ярлыки в Windows, но без хрупкости — жёсткие ссылки остаются действительными даже после перемещения оригинального файла.
С другой стороны, символические ссылки перестают работать, если целевой файл перемещается, что кажется значительным недостатком.
Единственное важное преимущество символических ссылок, которое я обнаружил, заключается в том, что они могут охватывать различные файловые системы, тогда как жёсткие ссылки ограничены одной файловой системой.
Исходя из этого, почему символические ссылки являются стандартом в большинстве случаев?
Какие практические сценарии, где символические ссылки предпочтительнее жёстких ссылок?
Помимо ограничений жёстких ссылок, которые вы уже затронули:
Обратите внимание, что они просто делают разные вещи; вы не можете заменить одну на другую в большинстве случаев. Символическая ссылка действительно содержит информацию о целевом пути, и это имеет огромное значение: сможем мы получить доступ к пути или нет, зависит от всех компонентов в пути, а не только от конечного файла. Так что семантика совершенно разная. Обычно вы хотите знать, какой путь вы “на самом деле” открываете, поэтому используете символическую ссылку. Честно говоря, я почти не могу найти ни одного случая, когда я бы предпочёл не знать, где ещё в моей файловой системе можно найти те же (изменяемые!!) данные.
Каждый раз прихожу к выводу, что жёсткие ссылки выглядят лучше в большинстве случаев, особенно когда Linux — моя основная система
Я бы сказал, что это ваше (одинаково валидное) мнение. Но для меня жёсткие ссылки — это один из грехов раннего проектирования файловой системы UNIX, и их не должно существовать, потому что они очень запутанные и ломают большинство предположений, которые вы могли бы сделать о файле. Они являются вечным источником условий гонки, и Linux особенно плохо подготовлен для работы с ними¹.
Путь к файлу — это идентификатор данных. Жёсткая ссылка это нарушает.
Например, когда вы просите libreoffice открыть /home/pie/doc.odt, он просит Linux открыть файл по пути “/home/pie/doc.odt” и предоставить ему дескриптор. Вы редактируете строку в этом документе и нажимаете “сохранить”. То, что происходит, не является чем-то особенным для libreoffice: большинство программ работают именно так. Libreoffice, вместо того чтобы запутывать оригинальный файл, потенциально ломая его на несколько мгновений перед завершением, создаёт новый временный файл, полностью записывает в него, а затем переименовывает его в “/home/pi/doc.odt”. Linux гарантирует, что это переименование является атомарным: когда вы открываете тот же путь до завершения переименования, это старое содержимое, после — новое. В любой момент времени вы не получите “недописанный” файл.
Жёсткая ссылка полностью нарушает это. Попробуйте сами:
# создаём два файла с разным содержимым
echo a_content > a_file
echo b_content > b_file
# создаём жёсткую ссылку
ln a_file a_linked
# проверяем содержимое
cat a_file b_file a_linked
# выведет
# a_content
# b_content
# a_content
# заменяем a на b, переименовав "b" в "a"
mv b a
# проверяем содержимое
cat a_file b_file a_linked
# выведет
# b_content
# b_content
# a_content
### Упс!
Не то, что ожидал пользователь.
Кроме того, они могут работать только в пределах одной и той же файловой системы, и это не очень хорошо, потому что это нарушает ещё одну абстракцию POSIX, а именно, что файл ведёт себя одинаково, независимо от того, куда его перемещают. Имеете жёстко связанную файл, которая меняется в унисон с другими её проявлениями в другом каталоге той же файловой системы? Теперь вы перемещаете её куда-то ещё, и вдруг ссылка исчезает. Вы, как пользователь, даже не можете знать, что это произошло, у вас может не быть привилегий, чтобы узнать, находятся ли два каталога на одной и той же файловой системе.
На системе Unix “файл” по сути является адресом, указывающим на данные на диске.
Я бы сказал, что на системе Unix файл — это часть данных, хранящаяся в структурированном виде, у которой может быть путь. (или, с жёсткими ссылками, несколько путей)
Например, если я создаю жёсткую ссылку на файл в ~/Documents, я могу получить доступ к тем же данным из ~/Desktop, и если я перемещу файл из ~/Documents в ~/Images, жёсткая ссылка продолжит работать без проблем.
Как объяснено выше, это плохо работает, как только вы осознаёте, что вы фотограф, ваш ~/Images — это ваше заработанное дело, так что вы помещаете это на отдельную кучу зеркал с дисками, или заканчивается место и вы помещаете это куда-то ещё, или что-то в этом роде.
Это поведение напоминает мне ярлыки в Windows, но без хрупкости — жёсткие ссылки остаются действительными даже после перемещения оригинального файла.
Это большое разрушение доверия, верно? Если я пишу счета с иллюстрациями, и в конце 2024 года я переместил ~/Documents/invoices/*.html и ~/Documents/invoices/*.png в ~/Documents/invoices/archive/2024/, то я ожидаю, что мои чёртовы счета не будут вдруг выглядеть по-другому, когда я проверю, как я что-то иллюстрировал позже.
Если я работал в 2025 году над своим ~/Images/illustrations/simple-example.png, который я жёстко связал в ~/Document/invoices/simple-customer-simple-example.png ещё в 2024 году, то вдруг всё ломается.
Худшее в этом то, что у меня нет практического способа узнать, была ли жёсткая ссылка только в то, что уже было архивировано и неизменяемо (например, мой счёт за январь 2024 года использует ту же картину, что и ~/Document/archive/2023/December/customer.html), или это “опасная” жёсткая ссылка в моём рабочем пространстве и её нужно “отвязать”, создавая фактическую копию.
С другой стороны, символические ссылки перестают работать, если целевой файл перемещается, что кажется значительным недостатком.
Это большое преимущество – они действительно просто опознаваемы как ссылки на другие места. Если мне нужно, чтобы эта часть данных была согласованной с другой частью данных, то мне нужна фактическая копия.
В примере с libreoffice ваша программа могла бы проверить фактический путь данных и заменить это, атомарно, для всех. С жёсткими ссылками вы не можете даже проверить другие проявления тех же данных¹!
Какие практические сценарии, где символические ссылки предпочтительнее жёстких ссылок?
Практически везде, именно потому что аспект “не может быть на разных файловых системах” делает всё супер хрупким, большинство стратегий упаковки пытаются сделать системы, работающие независимо от того, хранятся ли они на одном диске или распределены по многим файловым системам, и потому что “страшное действие на расстоянии” – это кошмар на практике.
¹ Не вдаваясь в подробности: вы могли бы подумать, что операционная система, которая решает работать с жёсткими ссылками на уровне файловой системы, имела бы API для работы с ними. Но: их нет.
Linux может сказать вам, что у файла есть N “проявлений” (по умолчанию, только 1). (ls -l
имеет колонку для этого.)
Но если вам нужно найти все проявления одного и того же файла, вам нужно
- Спросить у Linux, на какой файловой системе находится путь (на настольной системе, ваш пользователь, возможно, имеет право это делать. Если вы скрипт на сервере, шансы невелики, исследовать файловые системы сервера — это не то, что, например, нужно делать плагину wordpress, и вас блокируют)
- Найти все монтирования той же файловой системы (только root начальной пространства имен файловой системы), которые могут иметь разные опции монтирования, монтируя различные подмножества файлов. Это могут быть буквально сотни, например, на сервере, где запускаются контейнеры.
- на файловой системе с подтомами (скажем,
btrfs
,zfs
, несколько других), у вас может не быть общего тома-предка, смонтированного где-либо, так что другие “концы” жёсткой ссылки могут быть даже не видны. Смонтируйте том-предок только для чтения. Только root может это сделать. - Спросить у Linux номер инода файла, который вы знаете, что он жёстко связан где-то ещё
- С этим номером пройтись по всем этим монтированиям и убедиться, что вы действительно проверяете все файлы на этой файловой системе, имеют ли они тот же инод
Это, конечно, очень проблематично. Ваш оригинальный путь может быть удалён, пока вы ищете другие проявления. “Просмотр всех файлов” обычно займет от нескольких секунд до недель (на действительно больших файловых системах, подумайте об архивации кластеров хранения данных; тогда это также будет стоить настоящих денег, потому что вы используете энергию и изнашиваете носители холодного хранения).
Я знаю о случае, когда система использовала жёсткие ссылки в своей собственной иерархии хранения только как средство обмена данными в многопроцессной системе, что означало, что ей не нужно было искать снаружи “дублирующие проявления”, когда она восстанавливалась после сбоя (она очищала жёсткие ссылки при завершении). Идея заключалась в том, что системы A, B, C и D смотрят на одну и ту же файловую систему, говорят ей создать жёсткие ссылки в соответствии с изящной схемой именования, когда им нужно сообщить, что данные теперь доступны для обработки на следующем этапе обработки, и следующий этап обработки удаляет ссылку с предыдущего шага, как только он начинает обрабатывать файл. Если что-то ломается, вам просто нужно увидеть, какие файлы имеют только 1 ссылку: это те, которые в данный момент обрабатываются. Так что восстановление после сбоя означает просмотр всех файлов и создание пар файлов, которые уже были обработаны на первом этапе, и ещё не на следующем.
Всё это хорошо и спокойно (если немного Руба Голдберга), пока D не завершит восстановление после сбоя быстрее, чем B, и не начнет удалять файлы, которые полностью обработаны, тем самым освобождая номера инодов для повторного использования C. Вдруг B становится очень запутанным касательно пар, потому что номера инодов абсолютно не гарантируют описание тех же данных в течение времени и могут быть повторно использованы.
Плюсы для жёстких ссылок и когда они лучше:
- если вам нужны два имени для одного и того же файла, но одно из них может быть удалено или переименовано
- если цель связывания — экономия места и/или уменьшение избыточных файлов, и отношение между ссылкой и целью не важно
Минусы для жёстких ссылок:
- Нет отношения источник/цель между жёсткими ссылками. Все жёсткие ссылки идентичны. Инод не хранит имена файлов, ссылающихся на него, так что единственный способ найти другие жёсткие ссылки на тот же файл — это искать по всей файловой системе.
- Некоторые файловые системы ограничивают количество жёстких ссылок, которые могут ссылаться на один и тот же файл, но практически это никогда не является проблемой.
- Инод хранит права доступа к файлу, так что вы не можете иметь две жёсткие ссылки на один и тот же файл с разными правами или владельцем. В некоторых случаях это может вызвать проблемы безопасности, когда пользователи связывают файлы в каталоги, которые владелец не может из них удалить. (Некоторые системы это блокируют.)
Когда символические ссылки лучше (или единственный вариант):
- если вы хотите ссылаться на файл на другой файловой системе
- если вы хотите ссылаться на каталог
- если отношение между двумя файлами важнее, чем доступ к данным, если цель удалена или переименована
- если информация, которую вы хотите сохранить и просмотреть, не содержание, а имя цели в ссылке
- если содержимое файла должно изменяться в зависимости от ситуации; примеры:
- символическая ссылка на файл в
/etc
в сетевой общедоступной файловой системе, где цель локальна, а символическая ссылка — нет - относительная ссылка на файл в другой директории, где текущая директория может быть перемещена и вы хотите сохранить отношение, а не содержимое (где может быть параллельная структура в новом месте)
- символическая ссылка на файл в
- Символические ссылки могут реализовать примитивную версию копирования при записи, где второй пользователь может создать теневую структуру каталогов символических ссылок на файлы другого пользователя, а затем удалить символическую ссылку и заменить ее копией файла перед редактированием его (или заставить редактор делать это автоматически).
Минусы символических ссылок:
- Символические ссылки могут быть хрупкими; если цель символической ссылки перемещена или переименована, ссылка разрывается.
- Символические ссылки на каталоги (или символические ссылки!) могут создать лабиринт запутанных путей, усложняя понимание, какой путь является реальным. (Все жёсткие ссылки являются реальными путями.)
- Символические ссылки на каталоги могут вызывать циклы, и наивные приложения, которые не проверяют их и проходят по ним вслепую (как vscode), могут попасть в бесконечные циклы.
- Символические ссылки печально известны тем, что вызывают проблемы безопасности, когда приложение может проверить права доступа символической ссылки, и затем другая программа может заменить символическую ссылку в гонке перед тем, как первое приложение её использует. (Для этого существует множество мер смягчения.)
Обратите внимание, что использование жёстких ссылок и символических ссылок сильно зависит от контекста, и конкретная функция может быть плюсом в одной ситуации и минусом в другой.
Чтобы дать ввод на вопрос, заданный в заголовке:
Почему символические ссылки более распространены, чем жёсткие ссылки в Unix/LINUX?
1. Потому что жёсткие ссылки не могут пересекать разные файловые системы, а это наиболее распространённое использование операционными системами. Например, в Ubuntu ls -l /var/lock /var/run
:
lrwxrwxrwx 1 root root 9 May 12 2020 /var/lock -> /run/lock
lrwxrwxrwx 1 root root 4 May 12 2020 /var/run -> /run
Старые версии Ubuntu использовали для размещения файлов блокировки и PID файлов под /var
, но теперь они размещены под /run
, и возможность символических ссылок пересекать файловые системы предоставляет обратно совместимость для программ и пользователей, пытающихся использовать старые пути.
Также, например, в MacOS ls -l /var
:
lrwxr-xr-x@ 1 root wheel 11 Feb 4 08:57 /var -> private/var
2. Потому что символические ссылки являются само документируемыми. Например, в Ubuntu ls -l /usr/bin/python*
:
lrwxrwxrwx 1 root root 7 Apr 15 2020 /usr/bin/python -> python2
lrwxrwxrwx 1 root root 9 Mar 13 2020 /usr/bin/python2 -> python2.7
-rwxr-xr-x 1 root root 3657904 Dec 9 11:35 /usr/bin/python2.7
lrwxrwxrwx 1 root root 9 May 12 2020 /usr/bin/python3 -> python3.8
-rwxr-xr-x 1 root root 5490520 Feb 4 07:02 /usr/bin/python3.8
или ls -l /dev/disk/by-uuid
:
lrwxrwxrwx 1 root root 10 Feb 20 15:15 1f15f348-0a07-46be-868c-63a373cbb33c -> ../../sda3
lrwxrwxrwx 1 root root 10 Mar 1 06:39 6239c1e2-1244-48f1-9219-9f1213cb4826 -> ../../sda1
lrwxrwxrwx 1 root root 9 Feb 20 15:15 d0274a61-66ee-4a94-94a2-07615697d7a1 -> ../../sdb
lrwxrwxrwx 1 root root 10 Feb 20 15:15 d0e1cf56-df39-4336-bf61-a0c7853259db -> ../../sda2
3. Потому что назначение не обязательно должно существовать в случае символической ссылки, поэтому программы и скрипты могут установить символическую ссылку, указывающую на файл/устройство, которое будет создано/установлено позже.
Я не утверждаю, что предыдущие (главным образом ориентированные на программирование) ответы не включали это, я просто делаю более короткое и сладкое резюме из трёх основных причин, по которым символические ссылки так часто используются.
.
Ответ или решение
Символические ссылки гораздо чаще встречаются в Unix/Linux системах, чем жесткие ссылки. Чтобы понять причины этого, необходимо разобраться в теории и применении этих технологий, учитывая также их преимущества и недостатки.
Теория
На уровне файловой системы Unix/Linux файл можно представить как указатель на данные, хранящиеся на диске. Жесткие ссылки позволяют нескольким именам файлов ссылаться на одни и те же данные. Например, если создать жесткую ссылку на файл в директории ~/Documents
, этот файл будет доступен и из директории ~/Desktop
. Если переместить оригинальный файл в другое место, его копии через жесткие ссылки останутся доступными.
С другой стороны, символические ссылки (или симлинки) представляют собой отдельные файлы, которые содержат путь к другим файлам или директориям. Таким образом, они фактически служат навигаторами, указывающими на другие места в файловой системе.
Примеры
Жесткие ссылки:
- Пример: Используя жесткую ссылку в
~/Desktop
для файла из~/Documents
, вы получите доступ к тем же данным, даже если файл будет перемещен в другую директорию. - Преимущество: Они остаются валидными, даже если оригинальный файл перемещен или переименован в пределах того же файлового объема.
Символические ссылки:
- Пример: Симлинк из
~/Desktop
на файл в~/Documents
утратит свою актуальность, если файл будет перемещен в другую директорию. Однако символическая ссылка может указывать на файлы или каталоги, находящиеся на других файловых системах. - Преимущество: Оставляет видимой и заглушённой структуру связи, удобен для настройки путей доступа, которые могут изменяться.
Применение
Почему символические ссылки более распространены?
-
Перекрестные файловые системы: Симлинки могут указывать на файлы, расположенные на других файловых системах или разделах диска. Это делает их особенно полезными в многодисковых конфигурациях, часто используемых в серверных и корпоративных средах. Жесткие ссылки этим ограничены — они действуют только в пределах одной файловой системы.
-
Гибкость и обратная совместимость: Символические ссылки непосредственно указывают на конкретные пути, которые могут быть изменены или обновлены без нарушения структуры системы в целом. Это облегчает переносимость и управление изменениями в файловой системе. Например, симлинки используются, чтобы поддерживать старые пути доступа на практике, когда структура директорий изменяется (например, с
/var/run
на/run
на современных Linux системах). -
Самодокументируемость: Симлинки наглядно отражают свою зависимость от конкретного пути. Для системных администраторов это делает структуру файловой системы более прозрачной и понятной. Например, в конфигурациях утилит или библиотек часто используются симлинки, чтобы указать, какая версия ПО активна в данный момент.
Практические сценарии
Когда стоит использовать символические ссылки?
- Когда необходимо обеспечить доступ к файлам в разных файловых системах.
- Когда требуется связать файл или директорию с другой для упрощенной структуры доступа.
- Когда нужно сохранить структуру и взаимоотношения файлов и директорий в обозримом виде.
Когда могут пригодиться жесткие ссылки?
- В ситуациях, когда критически важно, чтобы разные имена файлов всегда указывали на точно одни и те же данные, даже если один из них будет удалён или перемещён в пределах той же файловой системы.
- Если цель — минимизация использования дискового пространства за счет общих физических данных.
Заключение
Символические ссылки выступают в роли более гибкого и повсеместно признанного инструмента, особенно в сложных средах с разнообразными файловыми системами и потребностями в гибкой и наглядной конфигурации. Они предлагают больший контроль и понимание структуры мест назначения, что значительно облегчает управление сложными системами и упрощает задачу обеспечения обратной совместимости и наглядности файловой структуры. В то же время, жесткие ссылки находят своё применение в узкоспециализированных задачах, связанных с внутренними оптимизациями файловой системы.
Понимание этих особенностей помогает осознанно выбирать тип ссылки, соответствующий конкретным требованиям задачи, и позволяет эффективно управлять структурой файловой системы в Unix/Linux средах.