Администратору постоянно нужно, чтобы один и тот же файл был доступен под несколькими именами или в нескольких местах: общий конфиг, текущая версия бинарника, файл лога, на который смотрит сразу пара демонов. Linux даёт для этого два разных механизма - жёсткие и символические ссылки. Они выглядят похоже, но работают совершенно по-разному, и путаница между ними приводит к битым путям и потерянным данным. В этом уроке разберём, что такое инод, чем жёсткая ссылка отличается от символической, какие у каждой ограничения и как читать вывод ls -li.

Как это работает
В файловой системе само имя файла и сам файл - это разные сущности. Содержимое, права, владелец, размер, временные метки и адреса блоков с данными хранятся в структуре, которая называется инод (inode). У каждого инода есть номер, уникальный в пределах конкретной файловой системы. А вот имя файла живёт в каталоге: каталог - это просто таблица, которая сопоставляет человекочитаемые имена номерам инодов. Имени в самом иноде нет.
Из этого следует ключевая идея. Жёсткая ссылка (hard link) - это ещё одна запись в каталоге, указывающая на тот же самый инод. То есть это полноценное второе имя одного и того же файла, равноправное с первым. Нет никакого оригинала и копии - есть один инод и несколько имён. В иноде хранится счётчик ссылок (link count): сколько имён на него указывают. Файл физически удаляется и его блоки освобождаются только тогда, когда счётчик падает до нуля и при этом файл не открыт ни одним процессом. Команда rm на самом деле делает unlink - убирает одно имя и уменьшает счётчик.
Символическая ссылка (symlink, мягкая ссылка) устроена иначе. Это отдельный маленький файл со своим собственным инодом, внутри которого записан текстовый путь к цели. Когда вы обращаетесь к симлинку, ядро читает этот путь и перенаправляет вас по нему. Симлинк не знает и не интересуется номером инода цели - он хранит только строку пути. Поэтому симлинк гибкий: может указывать на другую файловую систему, на каталог, на ещё не существующий объект. И поэтому же он хрупкий: если цель переименовать, переместить или удалить, ссылка останется, но будет указывать в пустоту - это битая (dangling) ссылка.
Отсюда границы применимости. Жёсткая ссылка работает только в пределах одной файловой системы, потому что номера инодов уникальны лишь внутри неё - сослаться номером инода на файл с другого раздела невозможно. И жёсткие ссылки на каталоги обычно запрещены, чтобы не создавать циклы в дереве, которые сломали бы обход ФС. Симлинк свободен от обоих ограничений, но платит за это надёжностью.
Команды и примеры
Создание жёсткой ссылки - ln без ключей, сначала цель, потом новое имя:
Код: Выделить всё
echo "config v1" > /srv/app.conf
ln /srv/app.conf /srv/app.conf.bak
ls -li /srv/app.conf /srv/app.conf.bakКод: Выделить всё
131074 -rw-r--r-- 2 root root 10 Jun 14 10:00 /srv/app.conf
131074 -rw-r--r-- 2 root root 10 Jun 14 10:00 /srv/app.conf.bakСимволическая ссылка - тот же ln с ключом -s:
Код: Выделить всё
ln -s /srv/app.conf /etc/app/current.conf
ls -li /etc/app/current.confКод: Выделить всё
262150 lrwxrwxrwx 1 root root 13 Jun 14 10:05 /etc/app/current.conf -> /srv/app.confПоиск битых ссылок. Команда find одинакова в Debian/Ubuntu и RHEL/Fedora, отличий нет:
Код: Выделить всё
find /etc -xtype lКод: Выделить всё
stat current.conf # покажет цель (разыменует)
stat -L current.conf # явно по цели
ls -l current.conf # покажет саму ссылкуЧастые грабли
- Перепутать порядок аргументов ln. Память: как cp - сначала источник (цель ссылки), потом имя новой ссылки.
- Создать симлинк с относительным путём из неправильного каталога. ln -s app.conf link сохранит ровно строку app.conf, и ссылка будет работать, только если рядом окажется файл с таким именем. Для стабильности используйте абсолютный путь или явно стройте относительный.
- Пытаться сделать жёсткую ссылку между разделами и получить Invalid cross-device link. Проверьте через df, что цель и ссылка на одной ФС, иначе только -s.
- Думать, что cp скопирует файл, а на деле скопировать симлинк. cp по умолчанию следует за ссылкой; чтобы сохранить саму ссылку, нужен cp -P или cp -a (для архивирования дерева).
- Считать, что жёсткая ссылка экономит место на бэкапе между разделами. Hard link живёт в пределах одной ФС, для дедупликации файлов на одном носителе он подходит, но через границу раздела не работает.
- Удалять цель симлинка, забыв про сам симлинк - он молча превращается в битый и продолжает торчать в каталоге.
- Создайте файл: echo data > /tmp/orig.txt
- Сделайте жёсткую ссылку /tmp/hard.txt и символическую /tmp/soft.txt на него.
- Выполните ls -li для всех трёх и сравните номера инодов и счётчик ссылок.
- Перезапишите содержимое через одно из имён (echo new > /tmp/hard.txt) и проверьте, что видно через orig.txt.
- Удалите /tmp/orig.txt. Проверьте, что hard.txt всё ещё содержит данные, а soft.txt стал битым (cat /tmp/soft.txt).
- Найдите битую ссылку командой find /tmp -xtype l.
- Восстановите цель и убедитесь, что soft.txt снова работает.
- Что физически хранится в иноде, а что - в записи каталога?
- Почему нельзя создать жёсткую ссылку на файл, лежащий на другом смонтированном разделе?
- Что произойдёт со счётчиком ссылок инода после удаления одного из его жёстких имён, и когда освободятся блоки данных?
- Чем вывод ls -li для жёсткой и символической ссылки на один файл будет отличаться?
- Какой командой найти все битые символические ссылки в дереве каталога?
- Почему символическая ссылка может указывать на каталог и на другую ФС, а жёсткая - нет?