Жесткие и символические ссылки в Linux

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

Жесткие и символические ссылки в Linux

Жесткая ссылка

john@ubuntu:~$ touch  readme

john@ubuntu:~$ ls -li  readme

20318653   -rw-r—r— 1  john john 0  ноя.    1  01:32 readme

john@ubuntu:~$  ln readme readme.txt

john@ubuntu:~$ touch README

john@ubuntu:~$ ls -li readne readme.txt README

20318653 -rw-r—r— 2 john john 0 ноя. 1 01:32 readme

20319121 -rw-r—r— 1 john john 0 ноя. 1 01:33 README

20318653 -rw-r—r— 2 john john 0 ноя. 1 01:32 readme.txt

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

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

Счетчик имен файла

john@ubuntu:~$ ln readme read.me

john@ubuntu:~$  ls -li  read*

20318653 -rw-r—r—   3 john john 0 ноя. 1 01:32 readme

20318653 -rw-r—r—   3 john john 0 ноя. 1 01:32 read.me

20318653 -rw-r—r- —   3 john john 0 ноя. 1 01:32 readme.txt

john@ubuntu:~$ rm readme

john@ubuntu:~$  ls -li  read*

20318653 -rw-r—r—  2 john john 0 ноя. 1 01:32 read.me

20318653 -rw-r—r— 2 john john 0 ноя. 1 01:32 readme.txt

john@ubuntu:~$  rm readme.txt

john@ubuntu:~$  ls -li read*

20318653 -rw-r—r—  1  john john 0 ноя. 1 01:32 read.me

Удаление открытого файла

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

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

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

Команда df измеряет доступное (свободное, disk free) место на файловой системе указанного файла, тогда как команда du, наоборот, измеряет занимаемое (disk usage) указанным файлом место на его файловой системе.

john@ubuntu:~$ df -h .

Файл.система                        Размер   Использовано   Дост   Использовано%  Смонтировано в

/dev/mapper/ubuntu-root     455G                    400G       32G                          93% /

john@ubuntu:~$ du -sh astra-linux-l.3-special-edition-snolensk-disk3-devel.iso

2,8G astra-linux-l.3-special-edition-snolensk-disk3-devel.iso

john@@ubuntu:~$ rm astra-linux-l.3-special-edition-snolensk-disk3-devel.iso

john@ubuntu:~$ df -h .

Файл.система                        Размер   Использовано   Дост   Использовано%  Смонтировано в

/dev/mapper/ubuntu-root      455G                    400G      32G                           93% /

finn@ubuntu:~$ lsof astra-linux-l.3-special-editrion-snolensk-disk3-devel.iso

COMMAND    PID   USER   FD   TYPE     DEVICE           SIZE/OFF      NODE      NAME

fuseiso            16925     john   3r     REG         252,0        2947385344   20316584   astra-linux-1.3-special-edition-

snolensk-disk3-devel.iso

john@ubuntu:~$ kill 16925

john@ubuntu:~$ df -h .

Файл.система                        Размер   Использовано   Дост   Использовано%  Смонтировано в

/dev/mapper/ubuntu-root      455G                      397G     35G                           92% /

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

Имена каталогов

john@ubuntu:~$ mkdir folder

john@ubuntu:~$ ls -ldi folder

20357139   drwxr-xr-x 2  john john 4096 апр. 1 01:58 folder

john@ubuntu:~$ cd folder

john@ubuntu:~/folder$  ls -lai

итого  8

20357139 drwxr-xr-x   2 john john 4096 ноя. 1 01:58 .

20332580 drwxr-xr-x  4 john john 4096 ноя. 1 01:58  . .

john@ubuntu:~/folder$ mkdir child

john@ubuntu:~/folder$ cd child

john@ubuntu:~/folder/chlld$ ls -lai

итого 8

20357140 drwxr-xr-x 2 john john 4096 ноя. 1 02:01 .

20357139 drwxr-xr-x 3 john john 4096 ноя. 1 02:01 . .

john@ubuntu:~/folder/chlld$ cd . ./. .

john@ubuntu:~$  stat folder/

Файл: «folder/»

Размер: 4096             Блоков: 8                      Блок B/B:         4096          каталог

Устройство:   fc00h/64512d        Inode: 20357139                      Ссылки:   3

. . .                        . . .                            . . .

john@ubuntu:~$ rmdir folder

rmdlr: не удалось удалить «folder»: Каталог не пуст

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

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

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

Символическая ссылка

john@ubuntu:~$ ln -s read.me readme.1st

john@ubuntu:~$ ls -li read*

20318653   -rw-r—r—     1  john john  0 ноя. 1  01:32 read.me

20319944  lrwxrwxrwx  1  john john  6 ноя. 2 00:02 readme.1st -> read.me ★

В случае с символической ссылкой при удалении целевого файла сама ссылка будет указывать в никуда и называться «сиротой» (orhpan). Попытка прочитать такую ссылку приводит к странным, на первый взгляд, результатам: файл «существует» для команды ls, но команда просмотра содержимого Cat говорит об обратном. Ничего удивительного, если помнить, что ls работает с именами файлов, a cat — с их данными (которые действительно не существуют).

Сиротская ссылка

john@ubuntu:~$ rm read.me

john@ubuntu:~$ ls read*

readme.1st

john@ubuntu:~$ cat readme.1st

cat: readme.1st: Нет такого файла или каталога

john@ubuntu:~$ ls -l read*

Irwxrwxrwx 1  john john 6 ноя.         2 00:02 readme.1st -> read.me

john@ubuntu:~$ cat read.me

cat: read.me: Нет такого файла или каталога

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

Кольцевые ссылки

 

john@ubuntu:~$ ln -s readme.1st read.me

john@ubuntu:~$ ls -l read*
lrwxrwxrwx 1  john john  10 ноя.       2 00:42 read.me -> readme.1st
lrwxrwxrwx 1   john john      8 ноя.    2 00:04 readme.1st -> read.me
john@ubuntu:~$ cat read.me
cat: read.me: Слишком много уровней символьных ссылок

Основным назначением символических (и изначально, жестких) ссылок является «множественная каталогизация» файлов, т.е. разные наборы разных имен одних и тех же данных. Типичным примером использования ссылок является организация boot сценариев запуска системных служб при старте операционной системы. Сами сценарии располагаются в каталоге /etc/lnit.d, а в каталогах /etc/rcS.d, /etc/rc0.d, …, /etc/rc6.d размещены символические ссылки на эти сценарии, которые должны быть запущены с определенными параметрами при переключении состояния системы между так называемыми «уровнями исполнения» runlevel.

Сценарии запуска служб и их каталогизация по уровням исполнения

john@ubuntu:~$ ls -l /etc/rc?1.d

/etc/rc2.d:
lrwxrwxrwx  1 root root 20 окт. 12  2018 /etc/rc2.d/S19postgresql -> .. /init.d/postgresql
lrwxrwxrwx  1 root root 17 ноя. 31  2018 /etc/rc2.d/S20postflx -> ../init.d/postftx
/etc/rc6.d:
lrwxrwxrwx  1 root root 17 ноя. 31  2018 /etc/rc6.d/K20postftx -> . ./init.d/postftx
lrwxrwxrwx  1 root root 20 окт. 12  2018 /etc/rc6.d/K21postgresql -> ../init.d/postgresql

В этом примере сценарии postfix и postgresql имеют вторичные имена, начинающиеся с K в каталоге rc6.d, и другие вторичные имена, начинающиеся с S в каталоге rc2.d. Это символизирует необходимость запускать (start) службы postfix и postgresql при переключении системы на уровень исполнения № 2 и уничтожать (kill) процессы этих служб при переключении системы на уровень исполнения № 6.

Добавить комментарий