[ @теория @symlink @hardlink ]
Что такое файл?
Любой файл/каталог (каталоги тоже файлы, но особого формата) — это набор каких-то данных, которые записаны на одном секторе на диске. Или, если они большие, то на каком-то множестве секторов на жестком диске.
Мы привыкли идентифицировать файлы по их именам. А операционная система определяет файлы на диске через inode — Индексный Дескриптор — это структура данных в традиционных для ОС UNIX файловых системах вроде ext4. В этой структуре хранятся только метаданные файлов, каталогов или других объектов файловой системы. Непосредственно данные файлов/каталогов хранятся сами по себе, они могут находиться последовательно на нескольких секторах, а могут быть разбросаны по всему диску.
Метаданные — это только техническая информация о файле, без его имени и содержимого:
- Тип объекта (файл, каталог, символическая ссылка и т.д.)
- Права доступа (чтение, запись, выполнение)
- ID владельца и группы
- Размер файла
- Временные метки (создание, последняя модификация, последний доступ)
- Кол-во ссылок на файл
- Адреса блоков данных, где фактически хранится содержимое файла на диске
Когда файл создают или перемещают (то есть, удаляют из одного места и создают в другом), операционная система связывает имя файла/каталога с уникальным inode в каталоге. Точнее, само имя хранится в каталоге, а inode указывает на физические блоки, в которых находится содержимое файла. Это позволяет системе
- отличать содержимое файла от его имени
- и поддерживать несколько ссылок на один и тот же файл — каждая «ссылка на файл» указывает на один и тот же inode.
Увидеть inode файла:
ls -i /home/astenix/test.txt
Пример ответа:
7734848 /home/astenix/test.txt
Циферки — это указатель места (точнее, область) на диске — inode.
Собственно, на вопрос «Что такое файл?» единственно корректным ответом будет «Файл — это ссылка на inode».
Что такое ссылка на файл?
Есть условность, которую надо знать и учитывать — в Linux у каждого файла/каталога всегда только один inode, но может быть одновременно несколько имён.
Хардлинк
Каждое отдельное имя одного и того же файла называется hardlink, оно же «жёсткая ссылка». На диске набор информации в файле один, а имён для него (хардлинков) — несколько. И все они равноценны.
Можно сказать иначе: когда у одного файла есть два и более имени, которые указывают на один и тот же inode, то это хардлинк.
Зачем это нужно? Это даёт возможность обращаться к одному и тому же файлу из разных каталогов и из разных userspace да под разными именами. Предположим, что на сервере в общем доступе находится файл «dailyReports.txt» Я могу в своём /home сделать хардлинк на этот файл и назову его «Отчёт.txt»; другой пользователь на том же компьютере в своём /home назовёт этот же файл (точнее, ссылку к нему) — «Анекдоты.txt», третий назовёт тот же файл «Важные данные.txt»… Ну или не txt, а файл базы данных — sql. Файл один и тот же, а названия у него разные. И каждый пользователь, который будет редактировать как будто бы разные файлы, а на деле — один и тот же.
Внешне хардлинк на файл ничем не отличается от самого файла — видишь файл, открываешь или редактируешь файл. Если не знать, что перед тобой хардлинк, то ничего об этом не подскажет, при редактировании хардлинка происходит редактирование оригинального файла.
Если оригинальный файл будет переименован или перенесён в другой каталог — хардлинк на него останется тем же и будет открывать переименованный/перемещенный файл так, словно никто ничего не менял.
Если оригинальный файл будет удалён (inode будет помечен как «в этот сектор можно записывать новые данные») — хардлинк на него останется тем же и будет открывать удалённый файл так, словно никто ничего не менял.
В MC после редактирования оригинального файла при сохранении появляется вопрос — «Файл имеет жёсткие ссылки. Отсоединить перед сохранением?». Если сказать да — изменения в файле не будут отображаться при открытии файла через хардлинк. А хардлинк вроде бы откроет тот же файл на редактирование, но изменения в нём не будут отображаться (нет ссылки — нет изменений). На самом деле хардлинк начинает указывать на новый inode, и будет сохранять все изменения в новом файле. Вполне резонно, что может возникнуть путаница с тем, кто кто-то клянётся, что записал в файл что-то новое, а кто-то открывает этот же файл, но изменений не видит. Оба одновременно правы…
Хардлинки можно переносить в разные каталоги — при этом на жёстком диске сам файл/каталог, на который сделана ссылка, останется на месте.
И даже при удалении одного из имён/хардлинков, файл физически остаётся на диске, но только до тех пор, пока в системе есть другие имена для этого же файла.
Но если все пользователи, у которых есть хардлинки на этот файл, эти самые хардлинки у себя удалят — вот тогда и оригинальный файл будет удалён с сервера (или же с того места на жёстком диске, где он находился). Упс. Поэтому разумнее делать симлинки на файлы.
См. Common:HowTo:Сделать:hardlink
Симлинк
Symbolic link, он же сокращённо «симлинк» — это ссылка не на inode файла, а на имя файла (на хардлинк).
Внешне симлинк тоже не распознать — файл и файл, но в Midnight Commander они нарочно отображаются с дополнительными символами:
- у симлинка на файл первый символ в имени — “@” (собачка).
- у симлинка на каталога первый символ в имени — “~“ (тильда).
Симлинки, как и хардлинки, тоже можно свободно переносить в разные каталоги — оригинал останется на месте.
Основная разница между ними в том, что происходит с оригиналами inode при удалении хардлинка и симлинка. Если хардлинки делает один пользователь, и хардлинк тоже один, то при удалении хардлинка, вероятнее всего, оригинальный файл тоже будет удалён (inode будет помечен как свободный), это неоднозначно и иногда откровенно стрёмно. А при удалении симлинков с оригиналом файла ОДНОЗНАЧНО ничего не происходит:
- при чтении/редактировании симлинка открывается на чтение/редактирование оригинал,
- при переименовании или удалении симлинка оригинал остаётся на месте — однозначно и безопасно,
- при переименовании или удалении оригинального файла симлинк остаётся, но выглядит как файл, который «невозможно открыть», ведь имени, на которое было указание, больше нет. В MC в этом случае первый символ в названии симлинка превращается в восклицательный знак — удобная подсказка.
А если снова создать на месте старого файла новый файл, то симлинк снова заработает.
И да, симлинки тоже можно именовать как угодно — ссылка на оригинальный файл/каталог остаётся неизменной.
См. Common:HowTo:Сделать:symlink
Примеры
В Linux можно запускать любой скрипт в консоли из любого каталога как обычную команду, если сделать на нужный скрипт симлинк в каталоге с бинарниками.
Например, я собрал последовательнось команд для обновления Debian и положил его в файл „/home/mySettings/myScripts/debian/debian_update_and_upgrade.sh”.
В консоли можно запустить этот скрипт так:
/home/mySettings/myScripts/debian/debian_update_and_upgrade.sh
Или сперва зайти в каталог с файлом
cd /home/mySettings/myScripts/debian/
и уже оттуда его запустить:
./debian_update_and_upgrade.sh
А ещё можно сделать символическую ссылку на этот файл, положить её в какой-то каталог «внутри профиля пользователя» и вызывать её в консоли как консольную команду.
У симлинка может быть любое имя, отличное от оригинального файла, и иногда это приносит дополнительное удобство. Например, оригинал будет называться „debian_update_and_upgrade.sh”, а симлинк можно называть очень сокращенно, например, „debup” — расширение для файла в этом случае вообще не имеет значения.
Или „dp”.
Или „updatemazafaka”.
Или все сразу.
Другой сценарий: ссылки на программное обеспечение. Например, пишем программу, которая обращается к исполняемым файлам Java. Где они находятся?
В консоли при вызове
java
происходит пробег по нескольким каталогам и файлам — да, симлинк может ссылаться на другой симлинк, который ссылается на третий симлинк…
- Читаем /usr/bin/java — это не просто файл без расширения, это симлинк на /etc/alternatives/java
- Читаем /etc/alternatives/java — это симлинк на /usr/lib/jvm/java-17-openjdk-amd64/bin/java
- Читаем /usr/lib/jvm/java-17-openjdk-amd64/bin/java — это симлинк на /usr/lib64/ld-linux-x86-64.so.2
- Читаем /usr/lib64/ld-linux-x86-64.so.2 — это симлинк на /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 с набором дополнительных инструкций
- Читаем, если не надоело, файл /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2…
Версии java постоянно меняются, меняется и содержимое файлов, и их расположение. Но через систему симлинков на стороне пользователя вызов java всегда происходит через одну и ту же команду. А вызов
java --version
с ответом вроде
openjdk 17.0.13 2024-10-15
OpenJDK Runtime Environment (build 17.0.13+11-Debian-2deb12u1)
OpenJDK 64-Bit Server VM (build 17.0.13+11-Debian-2deb12u1, mixed mode, sharing)
это же чтение содержимого переменных из разных файлов, в том числе и из /usr/lib/jvm/java-17-openjdk-amd64/release:
IMPLEMENTOR="Debian"
JAVA_RUNTIME_VERSION="17.0.13+11-Debian-2deb12u1"
JAVA_VERSION="17.0.13"
JAVA_VERSION_DATE="2024-10-15"
…
При выпуске следующей версии — java-18 — исполняемые файлы уже будут находиться в каталоге с другим названием, и информацию о версии придется читать из другого файла в каталоге с другим названием — /usr/lib/jvm/java-18-openjdk-amd64/release Однако благодаря системе перекрёстных хард и симлинков команда обращения будет прежняя.
Третий пример характерен для древних времён, когда на дисках всегда было ОЧЕНЬ мало места. Есть набор файлов, которые находятся в разных каталогах. И надо собрать их них условный плэйлист (сделаем прямую аналогию с аудиофайлами). Можно скопировать все нужные нам файлы в один новый каталог, но это значит, что на диске станет меньше свободного места. А можно в этот новый каталог положить только симлинки на нужные файлы, которые лежат в разных каталогах — если таких файлов много, то получится однозначная экономия места на hdd.