Глава из книги Сага о FreeBSD
Алексей Федорчук
2008-11-19
Старый пользователь DOS/Windows, не погрязший окончательно в метафоре файлов-документов, возникшей в MacOS (где, надо отдать ей должное, метафора эта вполне оправдана) и внедренной в Windows, помнит о существовании типов файлов, отличающихся своими расширениями, — исполняемых (*.exe, *.com), пакетных (*.bat), текстовых (*.txt), изображений в различных форматах (*.tiff там, или *.gif) и так далее. И потому будет весьма удивлен, когда узнает, что в Unix'ах все они не типизированы никак. И действительно — как ОС может отличить их друг от друга, если имени файла (и, следовательно, расширения) в атрибутах его нет и в помине? В этом смысле говорят, что во FreeBSD нет понятия типизации файла.
На самом деле средства для различения файлов с контентом разного рода существуют и во FreeBSD, но это обычно прерогатива приложений, а не самой ОС, и о них сейчас речи не будет.
Тем не менее, под понятие файла в POSIX-системах подпадают столь разнородные объекты, что они неизбежно требуют классификации. Другое дело, что классификация эта основана на совершенно других принципах. А именно, выделяются следующие типы файлов (и тип в данном случае — это атрибут, описанный в соответствующем поле inode каждого файла):
Пользователю постоянно приходится иметь дело с обычными файлами и каталогами, довольно часто — с символическими ссылками, время от времени — с файлами устройств. А вот с каналами и сокетами он практически не сталкивается (по крайней мере, у меня, за все годы работы во FreeBSD, такой необходимости ни разу не возникло). Пропорционально этому мы и уделим внимание перечисленным типам файлов. Начнем мы, однако, не с обычных файлов, с коими пользователь общается наиболее тесно, а с каталогов — и сейчас же станет ясно, почему.
Итак, каталоги (по-английски directory) — тип файлов, в некотором смысле противопоставляемый всем иным типам оных. Это — специальные файлы, объединяющие другие файлы (и подкаталоги, называемые также вложенными каталогами) в обособленные друг от друга группы (по крайней мере, так они видятся пользователю в специальных программах управления файлами — файловых менеджерах).
В русскоязычной литературе по DOS/Windows каталоги обычно именуются директориями, а в последнее время в обиход вошел термин "папка" (по-английски folder). Не смотря на то, что он успел уже проникнуть и в наши сферы (по крайней мере, в KDE, Xfce и так далее), для Unix-систем термин "каталог" предпочтительнее, и я сейчас постараюсь объяснить, почему.
Принятая в Windows (и идущая исторически от MacOS) метафора каталога как папки с документами способна создать впечатление (и подчас его создает), что каталог — это некое физическое вместилище включенных в него файлов. Но это не так. Каталоги — суть не более чем списки имен входящих в них файлов и тех самых числовых идентификаторов, о которых речь шла в прошлом разделе (и по которым собственно данные и находятся системой). И потому метафора библиотечного каталога (или книжного оглавления) как нельзя лучше отражает физический смысл соответствующего понятия.
Роль каталогов трудно переоценить. Потому что имя файла в файловой системе FreeBSD существует только в составе каталога, и никак иначе — как мы помним, среди атрибутов собственно файла его нет. Собственно же содержимое файла ставится в соответствие имени через идентификатор его inode, подобно тому, как это осуществляется в базах данных. Механизм соотнесения имени файла, его метаданных и данных носит название жесткой ссылки (hardlink), чтобы отличить его от ссылок символических — файлов особого типа, о которых речь пойдет в следующем разделе. Количество жестких ссылок и есть значение поля счетчика в inode файла. Очевидно, что возможный его минимум для большинства типов файлов — единица.
Из сказанного следует несколько важных свойств имен файлов. Идентификатора устройства, несущего данный файл, в каталоге нет. И потому имя файла (то есть элемент включающего его каталога) может существовать только в той же физической файловой системе (разделе), что и inode, с которым он связан жесткой ссылкой (и, разумеется, в той же, что и собственно область данных файла).
Во-вторых, на один inode с его данными может быть создано произвольное количество жестких ссылок (вспомним о соответствующем атрибуте — счетчике ссылок). То есть файл с одним и тем же физическим содержанием может выступать под рядом имен, и все они будут равноправны между собой. Или, напротив, под одним и тем же именем входить в состав разных каталогов.
Редактирование файла, вызванного под любым из его имен или из разных каталогов (например, текста в текстовом редакторе) будет приводить к изменению области данных файла, не зависящей от имени, и, следовательно, содержимое любого из таких файлов-двойников будет оставаться идентичным. Более того, любое из имен такого файла можно удалить (то есть изменить содержимое включающего его каталога), что не окажет никакого влияния на остальные имена файла (и, тем более, на его реальное содержимое).
Подчеркну, что два имени файла, связанных жесткой ссылкой с одним и тем же inode, не являются копией один другого, поскольку при копировании файла создаются новые области метаданных и данных, которые в дальнейшем будут существовать абсолютно независимо друг от друга — то есть изменение содержимого копии или ее атрибутов никак не затронет исходный файл.
В третьих, удаление файлов в Unix происходит совершенно иначе, чем в DOS/Windows. А именно, файл считается удаленным, когда уничтожены все имена, ссылающиеся на идентификатор данного inode (то есть файл исключен из файловой системы), и закрыта последняя программа, к нему обращающаяся (то есть завершен процесс, загрузивший данные файла в память, и уничтожен индексный дескриптор файла в этом процессе). В описании атрибутов файла это выражается в том, что счетчик ссылок его inode обнуляется. Разумеется, сами по себе данные, составляющие содержание файла, физически могут продолжать существовать на диске, но для системы они уже недоступны. А поскольку содержание файла оторвано от его имени, восстановление случайно удаленного файла по фрагменту имени (на чем основаны DOS-утилиты типа UNERASE и UNDELETE) оказывается невозможным.
Пока любой файл открыт, то есть существует ссылающийся на него процесс, он продолжает существовать, даже если имя его исключено из всех каталогов, и может быть записан, скопирован, переименован, и т.д. То есть открытый каким либо процессом дескриптор данного файла — гарантия его существования, по крайней мере, до завершения процесса. Именно поэтому я ранее сказал, что файл не обязательно имеет имя: в случае удаления открытого файла из каталога он некоторое время существует как бы безымянным, для поддержания его бытия достаточно открытого дескриптора, ассоциированного с inode.
Однако вернемся к каталогам. Как мы только что увидели, любой файл доступен для системы в том только случае, если он включен, пардон за двойную тавтологию, в файловую систему. То есть имя его приписано к определенному каталогу. Но ведь каталог — это тоже файл, и он тоже должен быть приписан к некоему каталогу более высокого уровня, который носит название родительского. И этот родительский каталог (символически он обозначается как ..) — один из элементов любого, даже не содержащего как бы файлов, каталога. А второй его непременный элемент — это он сам, то есть текущий каталог (что символизируется так — .). То есть для каталога минимум возможных связей (значение поля счетчика в inode), в отличие от всех прочих типов файлов, равно 2.
Итак, каждый файл входит в какой-либо каталог, который, в свою очередь, включен в каталог более высокого уровня и так далее. Вплоть до самого высокого каталога, не вполне логично именуемого корневым, за которым зарезервирован символ /. Вся эта конструкция называется деревом файловой системы (хотя на иллюстрациях в большинстве книжек это дерево растет обычно сверху вниз), файловой системой просто (как скоро станет ясным, это — один из самых многозначных терминов во всей околокомпьютерной литературе) или, наконец, файловой иерархией (т.е. иерархией файлов и каталогов). На последнем термине мы и остановимся, а к разъяснению его сути обратимся в одной из следующих глав.
Корневому каталогу файловой системы всегда присваивается идентификатор 2. Очевидно, что он не имеет родительского каталога — выше него в файловой иерархии не лежит ничего. Тем не менее, счетчик его ссылок также имеет два значения — ибо он сам выступает в качестве своего родителя. То есть ссылка на родительский каталог — есть ссылка на него же.
Отдельные ветви файловой системы, расположенные на самостоятельных дисковых разделах, имеют свои корневые каталоги, также имеющие обычно в качестве идентификатора 2. Однако, поскольку они различаются идентификаторами устройств (то есть разделов), путаницы между ними и "главным" корневым каталогом не происходит (подробнее об этом речь пойдет в главе о файловой иерархии).