\chapter{Файловые системы}\label{FileSystems}

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

Ведь вы же не будете набирать вашу программу каждый раз при новом включении
компьютера. Правда, на \machname{PDP-11} такое еще было возможным; в
НГУ даже ходят легенды о людях, которые набирали с консоли в пультовом режиме
тетрис, и играли в него, пока сетевой файловый сервер висел.
А если программа -- это пакет типа \verb"CorelDraw!", исходный текст
которого занимает десятки тысяч строк,
а бинарный загрузочный модуль -- полтора мегабайта?

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

Именно эту работу по структурированию пользовательских данных берет на
себя модуль ОС, называемый \term{файловым менеджером}. Дисковые операционные
системы (ДОС) состоят по сути только из файлового менеджера и загрузчика
бинарных модулей.

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

{\bf Файл -- это совокупность данных, доступ к которой осуществляется по ее
имени.}

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

ОС семейства \sysname{Unix} трактуют понятие файла более широко -- там
файлом называется любой объект, имеющий имя в файловой системе.
Однако файлы, не являющиеся совокупностями данных (каталоги,
внешние устройства, псевдоустройства, именованные программные каналы,
семафоры \sysname{Xenix}), часто называют не простыми файлами, а
<<специальными>>.

Понятно, что магнитный диск или другое устройство памяти (кроме, пожалуй,
файлового сервера) чаще всего организует доступ к данным не по
их именам, а все-таки по адресам, например, по номеру сектора, дорожки
и поверхности диска. Поэтому, если система хочет предоставлять доступ по
именам, она должна хранить таблицу преобразования имен в адреса --
\termin{директорию}{directory}, или, как чаще говорят по-русски,
\term{каталог}.

В каталоге хранится имя файла и другая информация о файле, такая, как 
его размер и местоположение на диске.
Как правило, хранят также дату создания файла, дату его последней модификации,
а в многопользовательских системах -- идентификатор хозяина этого файла и 
права доступа к нему для других пользователей. Во многих файловых системах
эта информация хранится не в самом каталоге, а в специальной структуре данных
-- \term{иноде}, \term{метафайле} и т.д. В этом случае запись в каталоге 
содержит только имя и указатель на управляющую структуру файла.

Большинство современных 
операционных систем позволяет делать вложенные каталоги -- файлы, которые 
сами являются каталогами. В таких системах файл задается полным или
\termin{путев\'{ы}м {\rm именем}}{path name}, состоящим из цепочки
имен вложенных каталогов и имени файла в последней из них.

Совокупность каталогов, а также системных структур данных, отслеживающих
размещение файлов на диске и свободное дисковое пространство, называется
\termin{файловой системой}{ФС}. Иногда на диске размещается только одна
файловая система. Современные ОС часто позволяют размещать на одном
физическом диске несколько файловых систем, выделяя каждой из них
фиксированную часть диска. Такие части диска называются
\termin{разделами}{partition} или \termin{срезами}{slice}.

\section{Файлы с точки зрения пользователя}
\subsection{Формат имен файлов}

В различных ФС допустимое имя файла может иметь различную длину, и в
нем могут использоваться различные наборы символов. Так, в \sysname{RT-11}
и \sysname{RSX-11} имена файлов состоят из символов кодировки \standname{RADIX-50}
и имеют длину 9 символов: 6 символов -- собственно имя, а 3 -- расширение.
При этом имя имеет вид \verb'"XXXXXX.XXX"', но символ \verb"'.'" не является
частью имени -- это просто знак препинания. Предполагается, что расширение
должно соответствовать типу данных, хранящихся в файле: \verb".SAV" будет
именем абсолютного загружаемого модуля, \verb".FOR" -- программы на Фортране,
\verb".CRH" -- <<файлом информации о системном крахе", как было написано
в одном переводе руководства (попросту говоря, это посмертная выдача ОС,
по которой можно попытаться понять причину аварии).

В \sysname{CP/M} и ее потомках \sysname{MS~DOS-DR~DOS},
а также в \sysname{VMS} имена файлов хранятся в 8-битной ASCII-кодировке,
но почему-то разрешено использование только букв верхнего регистра, цифр и
некоторых печатаемых символов. При этом в системах линии \sysname{CP/M}
имя файла имеет 8 символов + 3 символа расширения, а в \sysname{VMS} как
имя, так и расширение могут содержать более 32 символов.

{\small Любопытно, что MS/DR~DOS при поиске в каталоге переводят в
верхний регистр имя, заданное пользователем, но оставляют как есть имя,
считанное из каталога. Строго говоря, это ошибка: если
мы создадим имя файла, содержащее буквы нижнего регистра,
то ни одна программа не сможет открыть или переименовать такой файл.

Одному из авторов довелось столкнуться с такой проблемой при попытке
прочитать дискету, записанную ОС \sysname{TC} (Экспериментальная
UNIXоподобная ОС для Паскаль-машины N9000). Проблему удалось решить только
при помощи шестнадцатиричного дискового редактора прямым редактированием
имен в каталогах. Возможно, существует и более элегантное решение,
но авторам не удалось его найти.
Использовать конструкцию \verb"*.*" бесполезно, потому что, в
действительности, операции над файлами, заданными таким образом, состоят
из двух операций: \verb"FindFirst/FindNext", возвращающей [следующее]
имя файла, соответствующее шаблону, и \verb"Open". \verb"FindFirst/FindNext"
возвращает недопустимое имя файла и \verb"Open" не может использовать его.
Программа \verb"CHKDSK" не возражает против имен файлов в нижнем
регистре. Строго говоря, это тоже ошибка.
Все остальные способы так или иначе сводятся к прямой (в обход ДОС)
модификации ФС.

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

Наибольшим либерализмом в смысле имен отличаются ОС семейства \sysname{Unix},
в которых имя файла может состоять из {\it любых} символов кодировки ASCII,
кроме символов \verb"'\000'" и \verb"'/'", например, из восьми
символов перевода каретки. При этом \verb"'\000'" является
ограничителем имени, а \verb"'/'" -- разделителем между именем каталога
и именем файла. Никакого разделения на имя и расширение нет, и
хотя имена файлов с программой на языке C заканчиваются \verb'".c"', а
объектных модулей -- \verb'".o"', точка здесь является частью имени.
Вы можете создать файл с именем \verb'"gcc-2.5.8.tar.gz"'.
В \sysname{UNIX SVR3} длина имени файла ограничена 14 символами, а
в \sysname{BSD UNIX}, \sysname{Linux} и \sysname{SVR4} -- только длиной блока
на диске, т.е. 512 байтами или более. При этом нулевой символ считается 
концом имени в каталоге.

{\small
Возможность использовать в именах неалфавитные символы типа перевода каретки
или \verb"ASCII EOT" кажется опасным излишеством. На самом деле:
\begin{enumerate}
\item Это не излишество а, скорее, упрощение -- из процедур, работающих
с именами, удалена проверка символа на <<допустимость>>.
\item Оно не столь уж опасно: такой файл всегда можно
переименовать. В некоторых случаях процесс набора имени файла в командной
строке превращается в нетривиальное упражнение, потому что shell (командный
процессор) рассматривает многие неалфавитные символы как команды. Но надо
отметить, что, правильно используя кавычки и символ \verb"'\'",
пользователь может передать команде аргумент,
содержащий {\it любые} символы ASCII, кроме \verb'\000'".
\end{enumerate}
}

В последнее время среди ОС стало модным поддерживать длинные имена файлов.
Отчасти это, возможно, связано с тем, что производители ПО для персональных
компьютеров осознали, что системы семейства \sysname{Unix} являются
потенциально опасными конкурентами, а длинные имена файлов традиционно
считаются одним из преимуществ этого семейства.

Например, \sysname{OS/2}, использующая
файловую систему \sysname{HPFS} (High Performance File System --
высокопроизводительная файловая система\footnote{\sysname{HPFS}
{\it действительно} имеет более высокую производительность, чем
файловая система \sysname{MS~DOS}, называемая \sysname{FAT};
в п. \ref{FAT} будет сказано, за счет чего.}),
поддерживает имена файлов длиной до 256 символов, содержащие печатаемые
символы и пробелы. Точка считается частью имени, как и в \sysname{UNIX},
и можно создавать имена, содержащие несколько точек.

При этом нужно отметить, что система при поиске файла
приводит к одному регистру все алфавитные символы в имени. С одной стороны,
это означает дополнительное удобство для пользователя -- при наборе имени
не нужно заботиться о регистре букв, с другой -- пользователь не
может создать в одном каталоге файлы \verb'"text.txt"' и \verb'"Text.txt"'.
Из-за этого, например, нельзя использовать принятое в \sysname{UNIX}
соглашение о том, что файл на языке \progname{C} имеет расширение \verb'".c"',
а на языке \progname{C++} -- \verb'".C"'.

Поддержка длинных имен файлов реализована и в наследнике линии
\sysname{CP/M-MS~DOS},  операционной системе \sysname{Windows~95},
ранее известной под кодовым именем \sysname{Chicago}.

Некоторые ОС, например, \sysname{RSX-11} и \sysname{VMS}, поддерживают
также номер версии файла. В каталоге может существовать несколько версий
файла с одним именем; если номер версии при открытии файла не задается,
то открывается последняя версия.

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

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

\subsection{Операции над файлами}

Большинство современных ОС рассматривают файл как неструктурированную
последовательность байт переменной длины. В стандарте \standname{POSIX}
над файлом определены следующие операции:

\begin{itemize}
\item \verb"int open(char * fname, int flags, mode_t mode)"
Эта операция <<открывает>> файл, устанавливая соединение между программой
и файлом. При этом программа получает \term{дескриптор файла} -- целое
число, идентифицирующее данное соединение. Фактически это индекс в системной
таблице открытых файлов для данной задачи. Все остальные операции используют
этот индекс для ссылки на файл.

Параметр \verb"char * fname" задает имя файла.
\verb"int flags" -- это битовая маска, определяющая режим открытия файла.
Файл может быть открыт только на чтение, только на запись и на
чтение и запись; кроме того, можно открывать существующий файл,
а можно пытаться создать новый файл нулевой длины.
Необязательный третий параметр \verb"mode" используется только
при создании файла и задает атрибуты этого файла.

\item \verb"off_t lseek(int handle, off_t offset, int whence)"
Эта операция перемещает указатель чтения/записи в файле.
Параметр \verb"offset" задает количество байт, на которое нужно сместить
указатель, а параметр \verb"whence" -- откуда отсчитывать смещение.
Предполагается, что смещение можно отсчитывать от начала файла
(\verb"SEEK_SET"), от его конца (\verb"SEEK_END") и от текущего
положения указателя (\verb"SEEK_CUR"). Операция возвращает положение
указателя, отсчитываемое от начала файла. Таким образом, вызов
\verb"lseek(handle, 0, SEEK_CUR)" возвратит текущее положение указателя,
не передвигая его.

\item \verb"int read(int handle, char * where, size_t how_much)"
Операция чтения из файла. Указатель \verb"where" задает буфер,
куда нужно поместить
прочитанные данные; третий параметр указывает, сколько данных надо считать.
Система считывает требуемое число байт из файла, начиная с указателя
чтения/записи в этом файле, и перемещает указатель к концу считанной
последовательности. Если файл кончился раньше, считывается столько данных,
сколько оставалось до его конца. Операция возвращает количество
считанных байт. Если файл открывался только для записи, вызов \verb"read"
возвратит ошибку.

\item \verb"int write(int handle, char * what, size_t how_much)"
Операция записи в файл. Указатель \verb"what" задает начало буфера данных;
третий параметр указывает, сколько данных надо записать.
Система записывает требуемое число байт в файл, начиная с указателя
чтения/записи в этом файле, заменяя хранившиеся на в этом месте данные,
и перемещает указатель к концу записанного блока. Если файл кончился раньше,
его длина увеличивается. Операция возвращает количество записанных байт.

Если файл открывался только для чтения, вызов \verb"write" возвратит ошибку.

\item \verb"int ioctl(int handle, int cmd, ...)";
\item \verb"int fcntl(int handle, int cmd, ...)"
Дополнительные операции над файлом. Первоначально, по-видимому,
предполагалось, что \verb"ioctl" -- это операции над самим файлом,
а \verb"fcntl" -- это операции над дескриптором открытого файла,
но потом историческое развитие несколько перемешало функции этих системных
вызовов. Стандарт \standname{POSIX} определяет некоторые операции как над
дескриптором, например дублирование (в результате этой операции мы получаем
два дескриптора, связанных с одним и тем же файлом), так и над самим файлом,
например, операцию \verb"truncate" -- обрезать файл до заданной длины.
В большинстве версий \sysname{Unix} операцию \verb"truncate" можно
использовать и для вырезания данных из середины файла. При считывании данных
из такой вырезанной области считываются нули, а сама эта область не
занимает физического места на диске.

Важной операцией является блокировка участков файла.
Стандарт \standname{POSIX} предлагает для этой цели
библиотечную функцию, но в системах семейства \sysname{Unix} эта
функция реализована через вызов \verb"fcntl".

Большинство реализаций стандарта \standname{POSIX} предлагает и свои
дополнительные операции. Так, в \sysname{Unix SVR4} этими операциями
можно устанавливать синхронную или отложенную запись\footnote{
Подробнее понятие отложенной записи обсуждается в п.~\ref{lazy_write}.}
и т.д.
\item \verb"caddr_t mmap(caddr_t addr, size_t len, int prot,"\\
      \verb"             int flags, int handle, off_t offset)"
Отображение участка файла в виртуальное адресное пространство процесса.
Параметр \verb"prot" задает права доступа к отображенному участку:
на чтение, запись и исполнение. Отображение может происходить
на заданный виртуальный адрес, или же система может выбирать адрес для
отображения сама.
\end{itemize}

Еще две операции выполняются уже не над файлом, а над его именем:
это операции переименования и удаления файла. В некоторых системах,
например в системах семейства \sysname{Unix}, файл может иметь
несколько имен, и существует только системный вызов для удаления имени.
Файл удаляется при удалении последнего имени.

Видно, что набор операций над файлом в этом стандарте очень похож
на набор операций над внешним устройством. И то и другое рассматривается
как неструктурированный поток байт. Для полноты картины следует сказать,
что основное средство межпроцессной коммуникации в системах семейства
\sysname{Unix} (\term{труба}) также представляет собой
неструктурированный поток данных. Идея о том, что большинство актов передачи
данных может быть сведено к байтовому потоку, довольно стара, но
\sysname{Unix} был одной из первых систем, где эта идея была приближена к
логическому завершению.

Примерно та же модель работы с файлами принята в \sysname{CP/M},
а набор файловых системных вызовов \sysname{MS~DOS} фактически
скопирован с вызовов \sysname{Unix v7}. В свою очередь, \sysname{OS/2}
и \sysname{Windows~NT} унаследовали принципы работы с файлами
непосредственно от \sysname{MS~DOS}.

Напротив, в системах, не имеющих \sysname{Unix} в родословной,
может использоваться несколько иная трактовка понятия файла.
Чаще всего файл трактуется как набор записей. Обычно система поддерживает
записи как постоянной длины, так и переменной. Например, текстовый файл
интерпретируется как файл с записями переменной длины, а каждой строке текста
соответствует одна запись. Такова модель работы с файлами в \sysname{VMS}
и в ОС линии \sysname{OS/360}-\sysname{MVS} фирмы \entname{IBM}.

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

\subsection{Тип файла}\label{resource_fork}

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

Например, с точки зрения системы исходный текст программы на языке
\progname{C} и документ в формате \LaTeX\ совершенно идентичны:
и то и другое представляет собой текстовый файл (или, если угодно,
файл с записями переменной длины). Однако, если мы попытаемся подать
наш документ на вход \progname{C}-компилятора, мы получим
множество синтаксических ошибок и никакого полезного результата.

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

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

{\small
Так, например \progname{Explorer} - пользовательская оболочка 
\sysname{Windows 95} и \sysname{Windows NT 4.0} - позволяет связать
ту или иную программу с файлами, имеющими определенное расширение,
например, программу \progname{MS Word} с файлами, имеющими расширение
\verb".DOC". Когда пользователь нажимает левой кнопкой мыши на иконке,
представляющей такой файл, то автоматически запускается \progname{MS Word}.
Эти же ассоциации доступны и из командной строки - можно напечатать
\verb"start Доклад.DOC" и опять-таки запустится \progname{MS Word}.
}

Такое связывание очень прост\'{о} в реализации и реализуется не только
в \progname{Explorer}, но и в простых текстовых оболочках вроде
\progname{Norton Commander}. От ОС при этом требуется только
дать возможность каким-то образом различать типы файлов.

Первые попытки ассоциировать с файлом признак типа были
сделаны еще в 60 гг. При этом идентификатор типа
добавлялся к имени файла в виде короткой, но мнемонической
последовательности символов -- \termin{расширения}{extension}.
В большинстве современных ОС расширение отделяется от имени
символом \verb"'.'", но проследить истоки этой традиции авторам не
удалось. При этом, например, файлы на языке \progname{C} будут иметь
расширение \verb'".c"', на \progname{C++} -- \verb'".C"',
а документы в формате \LaTeX\ -- \verb'".tex'".

В ОС семейства \sysname{Unix} имя файла может содержать несколько
символов \verb"'.'", и, таким образом, файл может иметь несколько
каскадированных расширений. Например, файл \verb'"main.C"' -- это
программа на языке \progname{C++}; \verb'"main.C.gz"' -- это программа
на языке \progname{C++}, упакованная архиватором \progname{GNU Zip} с
целью сэкономить место; \verb'"main.C.gz.crypt"' -- это программа, которую
упаковали и потом зашифровали, чтобы никто посторонний не смог ее прочитать;
наконец, \verb'"main.C.gz.crypt.uue"' -- это упакованная и зашифрованная
программа, преобразованная в последовательность печатаемых символов
кода ASCII, например, для пересылки по электронной почте.

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

Например, стандартный \term{драйвер компилятора} в системах семейства
\sysname{Unix} -- программа \verb"cc" -- определяет тип файла именно по
расширению. Командная строка

\begin{example}
\small
\begin{verbatim}

     cc main.C c-code.c asm-code.s obj-code.o\
          library.a -o program
\end{verbatim}
\end{example}

\noparskip будет интерпретироваться следующим образом:

\begin{description}
\item[{\verb"main.C"}:] текст на языке \progname{C++}. Его нужно пропустить
через препроцессор и откомпилировать компилятором \progname{C++},
а затем передать то, что получится, редактору связей.
Большинство компиляторов в \sysname{Unix} генерируют код
на ассемблере, то есть вывод компилятора еще нужно пропустить
через ассемблер.

\item[{\verb"c-code.c"}:] текст на языке \progname{C}. Он обрабатывается,
так же, как и \progname{C++}-программа, только вместо
компилятора \progname{C++} используется обычный компилятор \progname{C}.

\item[{\verb"asm-code.s"}:] программа на языке ассемблера. Её нужно
обработать ассемблером и получить объектный модуль.

\item[{\verb"obj-code.o"}:] объектный модуль, который непосредственно
можно передавать редактору связей.

\item[{\verb"library.a"}:] объектная библиотека, которую нужно использовать
для разрешения внешних ссылок наравне со стандартными библиотеками.
\end{description}

Многие ОС, разработанные в 70-е гг., такие как \sysname{RT-11},
\sysname{RSX-11}, \sysname{VAX/VMS}, \sysname{CP/M}, навязывают программисту
разделение имени на собственно имя и расширение, интерпретируя точку
в имени файла как знак препинания. В таких системах имя может содержать
только одну точку и соответственно иметь только одно расширение.
Напротив, в ОС нового поколения -- \sysname{OS/2}, \sysname{Windows~NT}
и даже в \sysname{Windows~95} -- реализована поддержка имен файлов
свободного формата, которые могут иметь несколько каскадированных
расширений, как и в \sysname{Unix}.

Однако никакие средства операционной системы не могут навязать прикладным
программам правил выбора расширения для файлов данных. Это приводит к
неприятным коллизиям. Например, почти все текстовые процессоры от
\progname{Лексикон} до \progname{Word 7.0} включительно используют
расширение файла \verb".doc" (сокращение от \term{document}),
хотя форматы файлов у различных процессоров и даже у разных версий
одного процессора сильно различаются.

Другая проблема связана с исполняемыми загрузочными модулями. Обычно
система использует определенное расширение для исполняемых файлов.
Так, \sysname{VMS}, \sysname{MS/DR~DOS}, \sysname{OS/2}, \sysname{MS~Windows}
и \sysname{Windows~NT} используют расширение \verb".exe": сокращение от
\termin{executable}{исполняемый}.
Однако по мере развития системы формат загрузочного модуля может изменяться.
Так, например, \sysname{OS/2~v3.0~Warp} поддерживает по крайней мере
пять различных форматов загрузочных модулей.
\begin{enumerate}
\item 16-разрядные сегментированные загрузочные модули: формат
\sysname{OS/2 1.x}.
\item 32-разрядные загрузочные модули, использующие
\termin{<<плоскую>>}{flat} модель памяти: формат \sysname{OS/2 2.x}.
\item 32-разрядные модули нового формата, использующие упаковку кода
и данных: формат \sysname{OS/2 3.x}.
\item \sysname{DOS}-овские \verb".exe" и \verb".com" модули.
\item Загрузочные модули \sysname{MS~Windows}.
\end{enumerate}

Для исполнения последних двух типов программ \sysname{OS/2} создает
задачу, работающую в режиме совместимости с \machname{8086}.
Эта задача запускает копию ядра \sysname{DOS} и, если это
необходимо, копию \sysname{MS~Windows}, которые уже выполняют загрузку
программы. Загрузочные модули всех трех <<родных>> форматов загружаются
системой непосредственно.

Так или иначе загрузчик должен уметь правильно распознавать все форматы.
При этом он не может использовать расширение файла: файлы всех перечисленных
форматов имеют расширение \verb".EXE".

Похожая ситуация имеет место в системах семейства \sysname{Unix},
где бинарные загрузочные модули и командные файлы вообще не имеют расширения.
При этом большинство современных версий системы также поддерживает несколько
различных исторически сложившихся форматов загрузочного модуля.

Разработчики \sysname{Unix} столкнулись с этой проблемой еще в 70 гг.
 В качестве решения они предложили использовать
\termin{магические числа}{magic number} -- соглашение о том, что
файлы определенного формата содержат в начале определенный байт или
последовательность байтов. Первоначально это были численные коды;
файл \verb"/etc/magic" содержал коды, соответствующие известным
типам файлов. Позднее в качестве магических чисел стали использоваться
длинные текстовые строки. Так, например, изображения в формате
\standname{Compuserve GIF 87a} должны начинаться с символов \verb"GIF87a".

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

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

\begin{example}

\begin{verbatim}

GIF87a -- это очень плохой формат
хранения изображений.
\end{verbatim}
\end{example}

\noparskip будет воспринят некоторыми программами как изображение в формате
\standname{Compuserve GIF 87a}, каковым он, безусловно, не является.

Пытаясь как-то решить проблему идентификации типа файла, разработчики
\sysname{Macintoch} отказались как от расширений, так и от магических
чисел. В \sysname{MacOS} каждый файл состоит из двух частей или
\termin{ветвей}{forks}: ветви данных (\term{data fork}) и ветви
ресурсов (\term{resource fork}). Кроме идентификатора типа файла,
ветвь ресурсов хранит информацию о:
\begin{itemize}
\item иконке, связанной с этим файлом;
\item расположении иконки в открытой \termin{папке}{folder};
\item программе, которую нужно запустить при <<открытии>> этого файла.
\end{itemize}

{\small
Еще дальше в этом же направлении пошли разработчики системы \sysname{OS/2}.
В этой системе с каждым файлом связан набор \termin{расширенных
атрибутов}{extended attributes}. Атрибуты имеют вид \verb"ИМЯ:Значение".
При этом значение может быть как текстовой строкой, так и блоком двоичных
данных произвольного формата и размера. Некоторые расширенные атрибуты
используются оболочкой \progname{Workplace Shell} (\verb"WPS")
как эквивалент ветви ресурсов в \sysname{MacOS}: для идентификации типа
файла, связанной с ним иконы и размещения этой иконы в открытом окне.
Тип файла идентифицируется текстовой строкой. Например, программа на
языке \progname{C} идентифицируется строкой \verb"C code".
Это резко уменьшает вероятность конфликта имен типов -- ситуацию, довольно
часто возникающую при использовании расширений или магических чисел.

Другие атрибуты могут использоваться для других целей. Например,
\progname{LAN Server} -- файловый сервер фирмы \entname{IBM} --
использует расширенные атрибуты для хранения информации о собственнике файла
и правах доступа к нему. Некоторые текстовые редакторы используют расширенные
атрибуты для хранения положения курсора при завершении последней сессии
редактирования, так что пользователь всегда попадает в то место, где он 
остановился в прошлый раз.

Кроме того, расширенные атрибуты могут использоваться и для хранения
сведений о назначении файла. В \sysname{OS/2} существуют предопределенные
расширенные атрибуты с именами \verb'Subject' (тема), \verb'Comment'
(комментарии) и \verb'Key phrases' (ключевые фразы), которые могут, например,
использоваться для поисков документов, относящихся к заданной теме.
К сожалению, такой поиск возможен только если создатель документа
позаботился присвоить этим атрибутам правильные значения...
}

\section{Монтирование файловых систем}

Прежде чем ОС сможет использовать файловую систему, она должна
выполнить над этой системой операцию, называемую
\termin{монтированием}{mount}.
В общем случае операция монтирования включает:
\begin{itemize}
\item проверку типа монтируемой ФС;
\item проверку целостности ФС;
\item считывание системных структур данных и инициализация соответствующего
модуля файлового менеджера (драйвера файловой системы);
\item в некоторых случаях -- модификацию ФС с тем, чтобы
указать, что она уже смонтирована -- устанавливается
так называемый \termin{флаг загрязнения}{dirty flag}.
Это может быть необходимо, если ФС неустойчива к сбоям%
\footnote{Устойчивые и неустойчивые к сбоям ФС обсуждаются в 
п.~\ref{FaultTolerance}.
Здесь мы отметим только, что большинство современных высокопроизводительных
ФС относятся к категории неустойчивых.}
В этом случае при ее размонтировании необходимо выполнить специальные
операции.

Если система пытается подмонтировать такую ФС и видит, что флаг загрязнения
установлен, это означает, что при последнем использовании ее
не размонтировали надлежащим образом. В этом случае перед монтированием
необходимо запустить программу починки ФС, которая выявит все проблемы,
возникшие при таком неправильном размонтировании. Чаще всего проблем нет,
но они {\it могут} возникнуть;
\item включение новой файловой системы в общее пространство имен.
В различных системах это делается различными способами.
\end{itemize}

Если мы монтируем ФС, размещенную на удаленной машине (\term{файловом сервере}),
то шаги 1 и 2 заменяются на установление соединения этим файловым сервером.

Многие пользователи \sysname{MS/DR~DOS} никогда не сталкивались с понятием
монтирования. Дело в том, что эта система (как и многие другие ДОС,
например \sysname{RT-11}) выполняет упрощенную процедуру монтирования при
каждом обращении к файлу. Упрощения состоят в пропуске шагов 1 и 2 и
отсутствии шага 4 (ФС \sysname{MS/DR~DOS} устойчива к сбоям).

При работе с файловыми серверами \sysname{Novell Netware} монтирование
серверных файловых систем производится командой \verb"MAP".

Обычно имя файла в подмонтированной файловой системе имеет вид
\verb"ИМЯ_ФС:\имена\каталогов\имя.файла"
При этом вместо разделителей \verb"':'" и \verb"'\'" могут
использоваться другие символы.

В \sysname{RT-11}, \sysname{RSX-11} и \sysname{VMS} в качестве имени файловой системы
используется имя физического устройства, на котором размещена ФС.
Если используется DECNet, перед именем устройства можно поместить имя
узла сети, на котором это устройство находится. Полное имя файла в
\sysname{VMS} выглядит так: \verb"DUA0:[USERS.FAT_BROTHER.WORK]test.exe".
При этом \verb"DUA0" означает дисковое устройство 0, присоединенное к
дисковому контроллеру \verb"A": \verb"Disk Unit A [device #] 0",
\verb"USERS.FAT_BROTHER.WORK]" означает каталог \verb"WORK" в каталоге
\verb"FAT_BROTHER" в каталоге \verb"USERS".

В \sysname{CP/M, MS/DR~DOS} и \sysname{OS/2} имена файловых систем
обозначаются буквами латинского алфавита, а сами файловые системы
часто почему-то называются <<драйвами>>.
При некотором желании можно использовать в качестве имен ФС также
символы \verb"'['" и \verb"']'".
Устройства \verb"A:" и \verb"B:" -- это всегда дисководы гибких дисков%
\footnote{Любопытная деталь -- 3.5' диск совсем не гнется. Тем не менее,
он, почему-то, называется гибким.}; устройство \verb"C:" -- обычно
первый жесткий диск, или первый раздел на первом жестком диске.

{\small
Авторов всегда интересовал вопрос: что будет делать пользователь,
когда у него кончатся доступные буквы алфавита?
При использовании только локальных дисков такая ситуация кажется
маловероятной, но при подключении к нескольким файловым серверам
количество используемых файловых систем резко возрастает\ellipsis

В \sysname{OS/2}, \sysname{Windows~NT} и \sysname{Windows for Workgroups}
эта проблема решена использованием так называемых \progname{UNC}-имен,
задающих имя файла в виде \verb"\\NODE\SHARE\PATH\FILE.NAM",
где \verb"NODE" -- это имя сетевого узла, \verb"SHARE" -- имя разделяемого
ресурса на этом узле (это может быть не только разделяемый каталог, но и
принтер, а в \sysname{OS/2} также и модем), а \verb"PATH\FILE.NAM" -- путь
к файлу относительно разделяемого каталога. К сожалению, далеко не все
старые программы понимают такие имена. Например, даже стандартный командный
процессор системы \sysname{Windows NT} не может исполнить команду 
\verb"cd \\NODE\SHARE\DIR".
}

ДОС, как правило, помещают в пространство имен все доступные 
блочные устройства, не выполняя полной процедуры монтирования. 
Если какое-то из этих устройств не содержит ФС известного типа, 
то система будет возмущаться при обращениях к такому устройству,
но не удалит его из списка доступных ФС.
А иногда даже не будет возмущаться -- попробуйте поставить в дисковод
машины под управлением \sysname{MS/DR DOS} дискету, не содержащую
файловой системы (например, созданную программой \verb"tar") и
сказать \verb"DIR A:". Скорее всего, вы увидите несколько экранов мусора,
но ни одного сообщения об ошибке!

В операционных системах семейства \sysname{Unix} смонтированные ФС
выглядят как каталоги единого дерева\footnote{Строго говоря,
структура каталогов в \sysname{UNIX} не обязана являться деревом; но об
этом ниже}, начинающегося с корневого каталога, выделенной первой ФС,
называемой {\it корневой} ({\it root}). Администратор системы может
подмонтировать новую ФС к любому каталогу, находящемуся на любом
уровне дерева. Такой каталог после этого называют <<точкой
монтировки>>, но это выражение отражает только текущее состояние каталога.
После того как мы размонтируем ФС, мы сможем использовать этот каталог
как обычный, и наоборот, мы можем сделать точкой монтировки {\it любой}
каталог.

{\small
Такой подход имеет неочевидное, на первый взгляд, но серьезное преимущество
перед раздельными пространствами имен для разных физических файловых систем.
Преимущество состоит в том, что пространство имен оказывается <<отвязано>>
от физического размещения файлов. Поэтому администратор может поддерживать
неизменную структуру дерева каталогов, перемещая при этом отдельные ветви
по дискам ради более эффективного использования дискового пространства
или даже просто ради удобства администрирования.

По традиции все \sysname{Unix}-системы имеют примерно одинаковую
структуру дерева каталогов: системные утилиты находятся в каталоге
\verb"/bin", системные библиотеки -- в каталоге \verb"/lib",
конфигурационные файлы - в каталоге \verb"/etc" и т.д.
Например, база данных об именах пользователей всегда находится в файле
\verb"/etc/passwd".
}




