Реферат Структура файловой системы Linux и UNIX
Работа добавлена на сайт bukvasha.net: 2015-10-28Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
от 25%
договор
Основные понятия
Операционная система Linux разработана в соответствии с требованиями международного стандарта на UNIX-совместимые системы IEEE POSIX, поэтому логично будет кратко рассмотреть сначала структуру файловой системы ОС UNIX.
Одним из достоинств ОС UNIX является то, что система базируется на небольшом числе интуитивно ясных понятий. Однако, несмотря на простоту этих понятий, к ним нужно привыкнуть. Без этого невозможно понять существо ОС UNIX.
Пользователь
С самого начала ОС UNIX замышлялась как интерактивная система. Другими словами, UNIX предназначен для терминальной работы. Чтобы начать работать, человек должен "войти" в систему, введя со свободного терминала свое учетное имя (account name) и, возможно, пароль (password). Человек, зарегистрированный в учетных файлах системы, и, следовательно, имеющий учетное имя, называется зарегистрированным пользователем системы. Регистрацию новых пользователей обычно выполняет администратор системы. Пользователь не может изменить свое учетное имя, но может установить и/или изменить свой пароль. Пароли хранятся в отдельном файле в закодированном виде. Не стоит забывать свой пароль, снова узнать его не поможет даже администратор!
Все пользователи ОС UNIX явно или неявно работают с файлами. Файловая система ОС UNIX имеет древовидную структуру. Промежуточными узлами дерева являются каталоги со ссылками на другие каталоги или файлы, а листья дерева соответствуют файлам или пустым каталогам. Каждому зарегистрированному пользователю соответствует некоторый каталог файловой системы, который называется "домашним" (home) каталогом пользователя. При входе в систему пользователь получает неограниченный доступ к своему домашнему каталогу и всем каталогам и файлам, содержащимся в нем. Пользователь может создавать, удалять и модифицировать каталоги и файлы, содержащиеся в домашнем каталоге. Потенциально возможен доступ и ко всем другим файлам, однако он может быть ограничен, если пользователь не имеет достаточных привилегий.
Файловая система
Понятие файла является одним из наиболее важных для ОС UNIX. Все файлы, с которыми могут манипулировать пользователи, располагаются в файловой системе, представляющей собой дерево, промежуточные вершины которого соответствуют каталогам, и листья - файлам и пустым каталогам. Примерная структура файловой системы ОС UNIX показана на рисунке 2.1. Реально на каждом логическом диске (разделе физического дискового пакета) располагается отдельная иерархия каталогов и файлов. Для получения общего дерева в динамике используется "монтирование" отдельных иерархий к фиксированной корневой файловой системе.
Замечание: В мире ОС UNIX по историческим причинам термин "файловая система" является перегруженным, обозначая одновременно иерархию каталогов и файлов и часть ядра, которая управляет каталогами и файлами. Видимо, было бы правильнее называть иерархию каталогов и файлов архивом файлов, а термин "файловая система" использовать только во втором смысле. Однако, следуя традиции ОС UNIX, мы будем использовать этот термин в двух смыслах, различая значения по контексту.
Каждый каталог и файл файловой системы имеет уникальное полное имя (в ОС UNIX это имя принято называть full pathname - имя, задающее полный путь, поскольку оно действительно задает полный путь от корня файловой системы через цепочку каталогов к соответствующему каталогу или файлу; мы будем использовать термин "полное имя", поскольку для pathname отсутствует благозвучный русский аналог). Каталог, являющийся корнем файловой системы (корневой каталог), в любой файловой системе имеет предопределенное имя "/" (слэш). Полное имя файла, например, /bin/sh означает, что в корневом каталоге должно содержаться имя каталога bin, а в каталоге bin должно содержаться имя файла sh. Коротким или относительным именем файла (relative pathname) называется имя (возможно, составное), задающее путь к файлу от текущего рабочего каталога (существует команда и соответствующий системный вызов, позволяющие установить текущий рабочий каталог).
В каждом каталоге содержатся два специальных имени, имя ".", именующее сам этот каталог, и имя "..", именующее "родительский" каталог данного каталога, т.е. каталог, непосредственно предшествующий данному в иерархии каталогов.
Рис. 2.1. Структура каталогов файловой системы
UNIX поддерживает многочисленные утилиты, позволяющие работать с файловой системой и доступные как команды командного интерпретатора. Вот некоторые из них (наиболее употребительные):
cp имя1 имя2 | - копирование файла имя1 в файл имя2 |
rm имя1 | - уничтожение файла имя1 |
mv имя1 имя2 | - переименование файла имя1 в файл имя2 |
mkdir имя | - создание нового каталога имя |
rmdir имя | - уничтожение каталога имя |
ls имя | - выдача содержимого каталога имя |
cat имя | - выдача на экран содержимого файла имя |
chown имя режим | - изменение режима доступа к файлу |
Структура файловой системы
Файловая система обычно размещается на дисках или других устройствах внешней памяти, имеющих блочную структуру. Кроме блоков, сохраняющих каталоги и файлы, во внешней памяти поддерживается еще несколько служебных областей.
В мире UNIX существует несколько разных видов файловых систем со своей структурой внешней памяти. Наиболее известны традиционная файловая система UNIX System V (s5) и файловая система семейства UNIX BSD (ufs). Файловая система s5 состоит из четырех секций (рисунок 2.2,a). В файловой системе ufs на логическом диске (разделе реального диска) находится последовательность секций файловой системы (рисунок 2.2,b).
Рис. 2.2. Структура внешней памяти файловых систем s5 и ufs
Кратко опишем суть и назначение каждой области диска.
- Boot-блок содержит программу раскрутки, которая служит для первоначального запуска ОС UNIX. В файловых системах s5 реально используется boot-блок только корневой файловой системы. В дополнительных файловых системах эта область присутствует, но не используется.
- Суперблок - это наиболее ответственная область файловой системы, содержащая информацию, которая необходима для работы с файловой системой в целом. Суперблок содержит список свободных блоков и свободные i-узлы (information nodes - информационные узлы). В файловых системах ufs для повышения устойчивости поддерживается несколько копий суперблока (как видно из рисунка 2.2,b, по одной копии на группу цилиндров). Каждая копия суперблока имеет размер 8196 байт, и только одна копия суперблока используется при монтировании файловой системы (см. ниже). Однако, если при монтировании устанавливается, что первичная копия суперблока повреждена или не удовлетворяет критериям целостности информации, используется резервная копия.
- Блок группы цилиндров содержит число i-узлов, специфицированных в списке i-узлов для данной группы цилиндров, и число блоков данных, которые связаны с этими i-узлами. Размер блока группы цилиндров зависит от размера файловой системы. Для повышения эффективности файловая система ufs старается размещать i-узлы и блоки данных в одной и той же группе цилиндров.
- Список i-узлов (ilist) содержит список i-узлов, соответствующих файлам данной файловой системы. Максимальное число файлов, которые могут быть созданы в файловой системе, определяется числом доступных i-узлов. В i-узле хранится информация, описывающая файл: режимы доступа к файлу, время создания и последней модификации, идентификатор пользователя и идентификатор группы создателя файла, описание блочной структуры файла и т.д.
- Блоки данных - в этой части файловой системы хранятся реальные данные файлов. В случае файловой системы ufs все блоки данных одного файла пытаются разместить в одной группе цилиндров. Размер блока данных определяется при форматировании файловой системы командой mkfs и может быть установлен в 512, 1024, 2048, 4096 или 8192 байтов.
Монтируемые файловые системы
Файлы любой файловой системы становятся доступными только после "монтирования" этой файловой системы. Файлы "не смонтированной" файловой системы не являются видимыми операционной системой.
Для монтирования файловой системы используется системный вызов mount. Монтирование файловой системы означает следующее. В имеющемся к моменту монтирования дереве каталогов и файлов должен иметься листовой узел - пустой каталог (в терминологии UNIX такой каталог, используемый для монтирования файловой системы, называется directory mount point - точка монтирования). В любой файловой системе имеется корневой каталог. Во время выполнения системного вызова mount корневой каталог монтируемой файловой системы совмещается с каталогом - точкой монтирования, в результате чего образуется новая иерархия с полными именами каталогов и файлов.
Смонтированная файловая система впоследствии может быть отсоединена от общей иерархии с использованием системного вызова umount. Для успешного выполнения этого системного вызова требуется, чтобы отсоединяемая файловая система к этому моменту не находилась в использовании (т.е. ни один файл из этой файловой системы не был открыт). Корневая файловая система всегда является смонтированной, и к ней не применим системный вызов umount.
Как мы отмечали выше, отдельная файловая система обычно располагается на логическом диске, т.е. на разделе физического диска. Для инициализации файловой системы не поддерживаются какие-либо специальные системные вызовы. Новая файловая система образуется на отформатированном диске с использованием утилиты (команды) mkfs. Вновь созданная файловая система инициализируется в состояние, соответствующее наличию всего лишь одного пустого корневого каталога. Команда mkfs выполняет инициализацию путем прямой записи соответствующих данных на диск.
Интерфейс с файловой системой
Ядро ОС UNIX поддерживает для работы с файлами несколько системных вызовов. Среди них наиболее важными являются open, creat, read, write, lseek и close.
Важно отметить, что хотя внутри подсистемы управления файлами обычный файл представляется в виде набора блоков внешней памяти, для пользователей обеспечивается представление файла в виде линейной последовательности байтов. Такое представление позволяет использовать абстракцию файла при работе в внешними устройствами, при организации межпроцессных взаимодействий и т.д.
Файл в системных вызовах, обеспечивающих реальный доступ к данным, идентифицируется своим дескриптором (целым значением). Дескриптор файла выдается системными вызовами open (открыть файл) и creat (создать файл). Основным параметром операций открытия и создания файла является полное или относительное имя файла. Кроме того, при открытии файла указывается также режим открытия (только чтение, только запись, запись и чтение и т.д.) и характеристика, определяющая возможности доступа к файлу:
open(pathname, oflag [,mode])
Одним из признаков, которые могут участвовать в параметре oflag, является признак O_CREAT, наличие которого указывает на необходимость создания файла, если при выполнении системного вызова open файл с указанным именем не существует (параметр mode имеет смысл только при наличии этого признака). Тем не менее по историческим причинам и для обеспечения совместимости с предыдущими версиями ОС UNIX отдельно поддерживается системный вызов creat, выполняющий практически те же функции.
Открытый файл может использоваться для чтения и записи последовательностей байтов. Для этого поддерживаются два системных вызова:
read(fd, buffer, count) и write(fd, buffer, count)
Здесь fd - дескриптор файла (полученный при ранее выполненном системном вызове open или creat), buffer - указатель символьного массива и count - число байтов, которые должны быть прочитаны из файла или в него записаны. Значение функции read или write - целое число, которое совпадает со значением count, если операция заканчивается успешно, равно нулю при достижении конца файла и отрицательно при возникновении ошибок.
В каждом открытом файле существует текущая позиция. Сразу после открытия файл позиционируется на первый байт. Другими словами, если сразу после открытия файла выполняется системный вызов read (или write), то будут прочитаны (или записаны) первые count байтов содержимого файла (конечно, они будут успешно прочитаны только в том случае, если файл реально содержит по крайней мере count байтов). После выполнения системного вызова read (или write) указатель чтения/записи файла будет установлен в позицию count+1 и т.д.
Такой, чисто последовательный стиль работы, оказывается во многих случаях достаточным, но часто бывает необходимо читать или изменять файл с произвольной позиции (например, как без такой возможности хранить в файле прямо индексируемые массивы данных?). Для явного позиционирования файла служит системный вызов
lseek(fd, offset, origin)
Как и раньше, здесь fd - дескриптор ранее открытого файла. Параметр offset задает значение относительного смещения указателя чтения/записи, а параметр origin указывает, относительно какой позиции должно применяться смещение. Возможны три значения параметра origin. Значение 0 указывает, что значение offset должно рассматриваться как смещение относительно начала файла. Значение 1 означает, что значение offset является смещением относительно текущей позиции файла. Наконец, значение 2 говорит о том, что задается смещение относительно конца файла. Заметим, что типом данных параметра offset является long int. Это значит, что, во-первых, могут задаваться достаточно длинные смещения и, во-вторых, смещения могут быть положительными и отрицательными.
Например, после выполнения системного вызова
lseek(fd, 0, 0)
указатель чтения/записи соответствующего файла будет установлен на начало (на первый байт) файла. Системный вызов
lseek(fd, 0, 2)
установит указатель на конец файла. Наконец, выполнение системного вызова
lseek(fd, 10, 1)
приведет к увеличению текущего значения указателя на 10.
Естественно, системный вызов успешно завершается только в том случае, когда заново сформированное значение указателя не выходит за пределы существующих размеров файла.
Разновидности файлов
Как мы неоднократно отмечали, в ОС UNIX понятие файла является универсальной абстракцией, позволяющей работать с обычными файлами, содержащимися на устройствах внешней памяти; с устройствами, вообще говоря, отличающимися от устройств внешней памяти; с информацией, динамически генерируемой другими процессами и т.д. Для поддержки этих возможностей единообразным способом файловые системы ОС UNIX поддерживают несколько типов файлов, наиболее существенные из которых мы рассмотрим в этом разделе.
Обычные файлы
Обычные (или регулярные) файлы реально представляют собой набор блоков (возможно, пустой) на устройстве внешней памяти, на котором поддерживается файловая система. Такие файлы могут содержать как текстовую информацию (обычно в формате ASCII), так и произвольную двоичную информацию. Файловая система не предписывает обычным файлам какую-либо структуру, обеспечивая на уровне пользователей представление обычного файла как последовательности байтов. Используя базовые системные вызовы (или функции библиотеки ввода/вывода, которые мы рассмотрим в разделе 4), пользователи могут как угодно структурировать файлы. В частности, многие СУБД хранят базы данных в обычных файлах ОС UNIX.
Для некоторых файлов, которые должны интерпретироваться компонентами самой операционной системы, UNIX поддерживает фиксированную структуру. Наиболее важным примером таких файлов являются объектные и выполняемые файлы. Структура этих файлов поддерживается компиляторами, редакторами связей и загрузчиком. Однако, эта структура неизвестна файловой системе. Для нее такие файлы по-прежнему являются обычными файлами.
Файлы-каталоги
Наличие обычных файлов недостаточно для организации иерархических файловых систем. Требуется наличие каталогов, которые сопоставляют имена файлов или каталогов с их физическим описанием. Каталоги представляют собой особый вид файлов, которые хранятся во внешней памяти подобно обычным файлам, но структура которых поддерживается самой файловой системой.
Структура файла-каталога очень проста. Фактически, каталог - это таблица, каждый элемент которой состоит из двух полей: номера i-узла данного файла в его файловой системе и имени файла, которое связано с этим номером (конечно, этот файл может быть и каталогом). Если просмотреть содержимое текущего рабочего каталога с помощью команды ls -ai, то можно получить, например, следующий вывод:
inode File
number name
_________________________
33 .
122 ..
54 first_file
65 second_file
65 second_again
77 dir2
Этот вывод демонстрирует, что в любом каталоге содержатся два стандартных имени - "." и "..". Имени "." сопоставляется i-узел, соответствующий самому этому каталогу, а имени ".." - i-узел, соответствующий "родительскому" каталогу данного каталога. "Родительским" (parent) каталогом называется каталог, в котором содержится имя данного каталога. Файлы с именами "first_file" и "second_file" - это разные файлы с номерами i-узлов 54 и 65 соответственно. Файл "second_again" представляет пример так называемой жесткой ссылки: он имеет другое имя, но реально описывается тем же i-узлом, что и файл "second_file". Наконец, последний элемент каталога описывает некоторый другой каталог с именем "dir2".
Этот последний файл, как и любой обычный файл, хранится в файловой системе как набор блоков запоминающего устройства. Однако файловая система знает, что на самом деле это каталог со структурой, контролируемой файловой системой. Поэтому файлам-каталогам соответствует особый тип файла (обозначенный в их i-узлах), по отношению к которому возможно выполнение только специального набора системных вызовов:
mkdir, производящего новый каталог,
rmdir, удаляющий пустой (незаполненный) каталог,
getdents, позволяющего прочитать содержимое указанного каталога.
Отсутствует системный вызов, позволяющий прямо писать в файл-каталог. Какими бы правами вы не обладали по отношению к файлу-каталогу, прямая запись информации в него запрещена - прямое следствие фиксированной (и закрытой от пользователей) структуры файлов-каталогов. Запись в файлы-каталоги производится неявно при создании и уничтожении файлов и каталогов, однако читать из файла-каталога при наличии соответствующих прав можно (пример - стандартная утилита ls, которая как раз и пользуется системным вызовом getdents).
Специальные файлы
Специальные файлы не хранят данные. Они обеспечивают механизм отображения физических внешних устройств в имена файлов файловой системы. Каждому устройству, поддерживаемому системой, соответствует, по меньшей мере, один специальный файл. Специальные файлы создаются при выполнении системного вызова mknod, каждому специальному файлу соответствует порция программного обеспечения, называемая драйвером соответствующего устройства. При выполнении чтения или записи по отношению к специальному файлу, производится прямой вызов соответствующего драйвера, программный код которого отвечает за передачу данных между процессом пользователя и соответствующим физическим устройством.
При этом имена специальных файлов можно использовать практически всюду, где можно использовать имена обычных файлов. Например, команда
cp myfile /tmp/kuz
перепишет файл с именем myfile в подкаталог kuz рабочего каталога. В то же время, команда
cp myfile /dev/console
выдаст содержимое файла myfile на системную консоль вашей установки.
Различаются два типа специальных файлов - блочные и символьные (подробности см. в разделе 3.3). Блочные специальные файлы ассоциируются с такими внешними устройствами, обмен с которыми производится блоками байтов данных, размером 512, 1024, 4096 или 8192 байтов. Типичным примером подобных устройств являются магнитные диски. Файловые системы всегда находятся на блочных устройствах, так что в команде mount обязательно указывается некоторое блочное устройство.
Символьные специальные файлы ассоциируются с внешними устройствами, которые не обязательно требуют обмена блоками данных равного размера. Примерами таких устройств являются терминалы (в том числе, системная консоль), последовательные устройства, некоторые виды магнитных лент. Иногда символьные специальные файлы ассоциируются с магнитными дисками.
При обмене данными с блочным устройством система буферизует данные во внутреннем системном кеше. Через определенные интервалы времени система "выталкивает" буфера, при которых содержится метка "измененный". Кроме того, существуют системные вызовы sync и fsync, которые могут использоваться в пользовательских программах, и выполнение которых приводит к выталкиванию измененных буферов из общесистемного пула. Основная проблема состоит в том, что при аварийной остановке компьютера (например, при внезапном выключении электрического питания) содержимое системного кеша может быть утрачено. Тогда внешние блочные файлы могут оказаться в рассогласованном состоянии. Например, может быть не вытолкнут супер-блок файловой системы, хотя файловая система соответствует его вытолкнутому состоянию. Заметим, что в любом случае согласованное состояние файловой системы может быть восстановлено (конечно, не всегда без потерь пользовательской информации).
Обмены с символьными специальными файлами производятся напрямую, без использования системной буферизации.
Связывание файлов с разными именами
Файловая система ОС UNIX обеспечивает возможность связывания одного и того же файла с разными именами. Часто имеет смысл хранить под разными именами одну и ту же команду (выполняемый файл) командного интерпретатора. Например, выполняемый файл традиционного текстового редактора ОС UNIX vi обычно может вызываться под именами ex, edit, vi, view и vedit.
Можно узнать имена всех связей данного файла с помощью команды ncheck, если указать в числе ее параметров номер i-узла интересующего файла. Например, чтобы узнать все имена, под которыми возможен вызов редактора vi, можно выполнить следующую последовательность команд (третий аргумент команды ncheck представляет собой имя специального файла, ассоциированного с файловой системой /usr):
$ ls -i /usr/bin/vi
372 /usr/bin/vi
$ ncheck -i 372 /dev/dsk/sc0d0s5
/dev/dsk/sc0d0s5:
372 /usr/bin/edit
372 /usr/bin/ex
372 /usr/bin/vedit
372 /usr/bin/vi
372 /usr/bin/view
Ранее в большинстве версий ОС UNIX поддерживались только так называемые "жесткие" связи, означающие, что в соответствующем каталоге имени связи сопоставлялось имя i-узла соответствующего файла. Новые жесткие связи могут создаваться с помощью системного вызова link. При выполнении этого системного вызова создается новый элемент каталога с тем же номером i-узла, что и ранее существовавший файл.
Начиная с "быстрой файловой системы" университета Беркли, в мире UNIX появились "символические связи". Символическая связь создается с помощью системного вызова symblink. При выполнении этого системного вызова в соответствующем каталоге создается элемент, в котором имени связи сопоставляется некоторое имя файла (этот файл даже не обязан существовать к моменту создания символической связи). Для символической связи создается отдельный i-узел и даже заводится отдельный блок данных для хранения потенциально длинного имени файла.
Для работы с символьными связями поддерживаются три специальных системных вызова:
readlink - читает имя файла, связанного с именуемой символической связью (это имя может соответствовать реальному файлу, специальному файлу, жесткой ссылке или вообще ничему); имя хранится в блоке данных, связанном с данной символической ссылкой;
lstat - аналогичен системному вызову stat (получить информацию о файле), но относится к символической ссылке;
lchowm - аналогичен системному вызову chown, но используется для смены пользователя и группы самой символической ссылки.
Именованные программные каналы
Программный канал (pipe) - это одно из наиболее традиционных средств межпроцессных взаимодействий в ОС UNIX. В русской терминологии использовались различные переводы слова pipe (начиная от "трубки" и заканчивая замечательным русским словом "пайп"). Мы считаем, что термин "программный канал" наиболее точно отражает смысл термина "pipe".
Основной принцип работы программного канала состоит в буферизации байтового вывода одного процесса и обеспечении возможности чтения содержимого программного канала другим процессом в режиме FIFO (т.е. первым будет прочитан байт, который раньше всего записан). В любом случае интерфейс программного канала совпадает с интерфейсом файла (т.е. используются те же самые системные вызовы read и write).
Однако различаются два подвида программных каналов - неименованные и именованные. Неименованные программные каналы появились на самой заре ОС UNIX (см. раздел 1.2). Неименованный программный канал создается процессом-предком, наследуется процессами-потомками, и обеспечивает тем самым возможность связи в иерархии порожденных процессов. Интерфейс неименованного программного канала совпадает с интерфейсом файла (более подробно см. п. 3.4.4). Однако, поскольку такие каналы не имеют имени, им не соответствует какой-либо элемент каталога в файловой системе.
Именованному программному каналу обязательно соответствует элемент некоторого каталога и даже собственный i-узел. Другими словами, именованный программный канал выглядит как обычный файл, но не содержащий никаких данных до тех пор, пока некоторый процесс не выполнит в него запись. После того, как некоторый другой процесс прочитает записанные в канал байты, этот файл снова становится пустым. В отличие от неименованных программных каналов, именованные программные каналы могут использоваться для связи любых процессов (т.е. не обязательно процессов, входящих в одну иерархию родства). Интерфейс именованного программного канала практически полностью совпадает с интерфейсом обычного файла (включая системные вызовы open и close), хотя, конечно, необходимо учитывать, что поведение канала отличается от поведения файла (подробности см. в п. 3.4.4).
Файлы, отображаемые в виртуальную память
В современных версиях ОС UNIX (например, в UNIX System V.4) появилась возможность отображать обычные файлы в виртуальную память процесса с последующей работой с содержимым файла не с помощью системных вызовов read, write и lseek, а с помощью обычных операций чтения из памяти и записи в память. Заметим, что этот прием был базовым в историческом предшественнике ОС UNIX - операционной системе Multics (см. раздел 1.1).
Для отображения файла в виртуальную память, после открытия файла выполняется системный вызов mmap, действие которого состоит в том, что создается сегмент разделяемой памяти, ассоциированный с открытым файлом, и автоматически подключается к виртуальной памяти процесса (подробнее о разделяемой памяти см. п. 3.4.1). После этого процесс может читать из нового сегмента (реально будут читаться байты, содержащиеся в файле) и писать в него (реально все записи отображаются в файл). При закрытии файла соответствующий сегмент автоматически отключается от виртуальной памяти процесса и уничтожается, если только файл не подключен к виртуальной памяти некоторого другого процесса.
Несколько процессов могут одновременно открыть один и тот же файл и подключить его к своей виртуальной памяти системным вызовом mmap. Тогда любые изменения, производимые путем записи в соответствующий сегмент разделяемой памяти, будут сразу видны другим процессам.
Синхронизация при параллельном доступе к файлам
Исторически в ОС UNIX всегда применялся очень простой подход к обеспечению параллельного (от нескольких процессов) доступа к файлам: система позволяла любому числу процессов одновременно открывать один и тот же файл в любом режиме (чтения, записи или обновления) и не предпринимала никаких синхронизационных действий. Вся ответственность за корректность совместной обработки файла ложилась на использующие его процессы, и система даже не предоставляла каких-либо особых средств для синхронизации доступа процессов к файлу.
В System V.4 появились средства, позволяющие процессам синхронизировать параллельный доступ к файлам. В принципе, было бы логично связать синхронизацию доступа к файлу как к единому целому с системным вызовом open (т.е., например, открытие файла в режиме записи или обновления могло бы означать его монопольную блокировку соответствующим процессом, а открытие в режиме чтения - совместную блокировку). Так поступают во многих операционных системах (начиная с ОС Multics). Однако, по отношению к ОС UNIX такое решение принимать было слишком поздно, поскольку многочисленные созданные за время существования системы прикладные программы опирались на свойство отсутствия автоматической синхронизации.
Поэтому разработчикам пришлось пойти "обходным путем". Ядро ОС UNIX поддерживает дополнительный системный вызов fcntl, обеспечивающий такие вспомогательные функции, относящиеся к файловой системе, как получение информации о текущем режиме открытия файла, изменение текущего режима открытия и т.д. В System V.4 именно на системный вызов fcntl нагружены функции синхронизации.
С помощью этого системного вызова можно установить монопольную или совместную блокировку файла целиком или блокировать указанный диапазон байтов внутри файла. Допускаются два варианта синхронизации: с ожиданием, когда требование блокировки может привести к откладыванию процесса до того момента, когда это требование может быть удовлетворено, и без ожидания, когда процесс немедленно оповещается об удовлетворении требования блокировки или о невозможности ее удовлетворения в данный момент времени.
Установленные блокировки относятся только к тому процессу, который их установил, и не наследуются процессами-потомками этого процесса. Более того, даже если некоторый процесс пользуется синхронизационными возможностями системного вызова fcntl, другие процессы по-прежнему могут работать с тем файлом без всякой синхронизации. Другими словами, это дело группы процессов, совместно использующих файл, - договориться о способе синхронизации параллельного доступа.
Распределенные файловые системы
Основная идея распределенной файловой системы состоит в том, чтобы обеспечить совместный доступ к файлам локальной файловой системы для процессов, которые, вообще говоря, выполняются на других компьютерах. Эта идея может быть реализована многими разными способами, однако в среде ОС UNIX все известные подходы основываются на монтировании удаленной файловой системы к одному из каталогов локальной файловой системы. После выполнения этой процедуры файлы, хранимые в удаленной файловой системе, доступны процессам локального компьютера точно таким же образом, как если бы они хранились на локальном дисковом устройстве.
На рисунке 2.5 приведен пример, в котором два подкаталога удаленной файловой системы-сервера (share и X11) монтируются к двум (пустым) каталогам файловой системы-клиента.
Рис. 2.5. Схема монтирования удаленной файловой системы
В принципе, такая схема обладает и достоинствами, и недостатками. К достоинствам, конечно, относится то, что при работе в сети можно экономить дисковое пространство, поддерживая совместно используемые информационные ресурсы только в одном экземпляре. Но, с другой стороны, пользователи удаленной файловой системы неизбежно будут работать с удаленными файлами существенно более медленно, чем с локальными. Кроме того, реальная возможность доступа к удаленному файлу критически зависит от работоспособности сервера и сети. Заметим, что распространенные в мире UNIX сетевые файловые системы NFS (Network File System - сетевая файловая система) и RFS (Remote File Sharing - совместное использование удаленных файлов) являются достаточно тщательно спроектированными и разработанными продуктами, во многом сглаживающими отмеченные недостатки.
Сетевая файловая система (NFS)
Система NFS была разработана компанией Sun Microsystems как часть ее сетевого продукта ONC (Open Network Computing - открытая сетевая вычислительная обработка). В настоящее время NFS является официальным компонентом UNIX System V Release 4.
NFS разрабатывалась как система, пригодная к использованию не только на разных аппаратных, но и на разных операционных платформах. В настоящее время продукт NFS в соответствии со спецификациями и на основе программного кода Sun Microsystems выпускает более 200 производителей. Отметим, в частности, наличие популярного в России продукта PC-NFS, обеспечивающего клиентскую часть системы в среде MS-DOS. Кроме того, заметим, что имеются и свободно доступные (public domain), и коммерческие варианты NFS.
Первоначально NFS разрабатывалась в среде UNIX BSD 4.2, и для реализации системы потребовалось существенно переделать код системных вызовов файловой системы. При внедрении NFS в среду System V понадобилась значительная переделка ядра ОС. Отмечается, что большая часть изменений в ядре System V Release 4 была связана именно с NFS.
В архитектурном отношении в NFS выделяются три основные части: протокол, серверная часть и клиентская часть. Протокол NFS опирается на примитивы RPC, которые, в свою очередь, построены над протоколом XDR (см. п. 2.7.4). Клиентская часть NFS взаимодействует с серверной частью системы на основе механизма RPC.
Основным достоинством NFS является возможность использования в среде разных операционных систем. Возможным недостатком является то, что независимость от транспортных средств ограничена уровнем такой независимости, присущей RPC. В настоящее время де-факто это означает, что NFS можно использовать только в TCP/IP-ориентированных сетях. (Это еще вопрос - плохо ли это, поскольку стимулирует использование единообразных сетевых механизмов.)
Совместное использование удаленных файлов (RFS)
Сетевая файловая система RFS была реализована компанией AT&T в рамках ее продукта UNIX System V Release 3. Функционально она выглядит подобно NFS, т.е. обеспечивает прозрачный доступ к удаленным файлам. Однако реализация системы абсолютно отлична. Основным недостатком RFS является то, что система реализуема только на компьютерах, работающих под управлением ОС UNIX (причем именно UNIX System V с номером выпуска не меньше, чем 3). Но с другой стороны, это решение позволило сохранить для пользователей RFS всю семантику файлов ОС UNIX. В частности (в отличие от NFS), в удаленной файловой системе могут находиться не только обычные файлы и каталоги, но и специальные файлы и именованные программные каналы. Более того, на удаленные файлы распространяются возможности блокировки файлов и/или диапазонов адресов внутри файлов.
Если NFS опирается на протокол RPC, то в RFS используется родной для AT&T протокол обмена сообщениями на основе потоков (другими словами, реализация RFS основана на использовании интерфейса TLI). (Кстати, в этом имеется большой смысл, поскольку механизм RPC во многих случаях является слишком ограничительным.)
Другим преимуществом RPC (тоже связанным с использованием TLI) является независимость системы от используемого транспортного механизма (если, конечно, этот механизм поддерживает спецификации семиуровневой модели ISO/OSI). Поэтому эту систему можно использовать в среде операционных систем, основывающихся на различных сетевых протоколах (ISO/OSI уважают практически все).
Теперь перейдем собственно к файловой системе Linjx/
Структура файловой системы
LINUX
Человеку, ранее работавшему с DOS или Windows, при общении с Linux прежде всего бросаются в глаза использование символа `/' вместо '\', отсутствие имен дисков (A:, B:, C: и т. д.) и то, что в именах файлов различаются большие и маленькие буквы. Однако другие особенности, не столь заметные с первого взгляда, более существенны. Давайте посмотрим, как устроена в Linux работа с файлами.
Прежде чем переходить к основному изложению, заметим, что выражение "файловая система" имеет два значения. Так называют, во-первых, определенный способ организации файлов, каталогов и т. д., а во-вторых, конкретное множество файлов, каталогов и т. д., организованное по этому способу.
Хотя Linux поддерживает более десятка самых разных файловых систем, все "иностранные" (foreign) системы так или иначе маскируются под стандартно используемую в этой ОС ext2fs. Давайте с нее и начнем.
Узлы и каталоги
Во времена юности Unix, когда автор Linux еще не ходил в школу, работа с файлами в этой ОС была организована весьма просто: все файлы на диске нумеровались и для каждого создавалась специальная запись - узел (inode; к сожалению, устоявшегося русского перевода этого термина пока нет), где содержалась служебная информация о файле. Имя в состав этой информации не включалось, а связывалось с узлом с помощью ссылки (link). Ссылки оформлялись как пары вида "имя файла - номер узла" и хранились в каталогах. Каталог можно было читать, как обычный файл, однако для его модификации уже тогда требовалось использовать специальные команды.
Найдя по ссылке в каталоге узел, ядро ОС дальше оперировало с файлом, не обращая никакого внимания на его исходное имя, поскольку все необходимые данные (размер файла, права доступа к нему и т. д.) извлекались уже из узла. С тех пор многое изменилось, однако в файловой системе Linux узлы по-прежнему играют важную роль, а вся информация о файле по-прежнему хранится отдельно от его имени.
Сколько имен у файла?
Описанная схема позволяет связать с одним файлом несколько равноправных имен, что в ряде случаев бывает полезно. Сравните, например, характеристики файлов unzip и zipinfo на рис. 1 и 2
Рис. 1. Характеристики файла unzip
(для получения информации использована программа Midnight Commander).
Рис. 2. Характеристики файла zipinfo
Как видим, они совпадают: у обоих файлов одно и то же "положение" (кстати, шестнадцатеричное число 1A328h является номером узла), одинаковая длина и т. д., причем на каждый из файлов имеется по две ссылки - это в действительности и есть два имени. Но, будучи физически одним и тем же файлом, unzip и zipinfo являются различными программами: каждая из них выполняет свои действия и имеет собственный набор опций. Никакой мистики здесь нет - просто в программу в качестве нулевого параметра передается ее имя, по которому она и определяет, что делать дальше.
Далее, в Unix-системах (включая Linux) нет операции удаления файла в смысле DOS/Windows: есть только удаление ссылки на узел - unlink - и ссылки на пустой каталог - rmdir (в некоторых Unix-системах unlink позволяет удалить ссылку на пустой каталог, но в Linux это не так). Сам же файл удаляется автоматически тогда, когда делается недоступным для системы. Это означает, что не должно остаться, во-первых, ни одной ссылки на него, а во-вторых, ни одной работающей с ним активной программы.
Именно поэтому, кстати, в Linux можно обойтись без перезагрузки практически при любом изменении системы (за исключением замены ядра). Чтобы обновить системную библиотеку, вы стираете ее прежнюю версию (т. е. освобождаете соответствующую ссылку) и записываете под тем же именем новую. Ядро откладывает момент удаления библиотеки до тех пор, пока не закончит работу последняя из программ, ее использующих (что может произойти много месяцев спустя). Это весьма удобно, но если не знать о таком поведении системы, можно попасть в неприятную ситуацию.
Монтирование
Однако если ядро ОС учитывает не только состояние файловой системы на диске, но и состояние программ, работающих с файлами, то как быть со сменными носителями? Ведь если просто вынуть из дисковода дискету с файлом, уже не имеющим имени, но еще не удаленным, файловая система будет разрушена; степень повреждения зависит от разных обстоятельств и варьирует от небольших неполадок до полного превращения в руины. Здесь как нельзя лучше подтверждается мысль о том, что наши недостатки - это продолжение наших достоинств.
Чтобы избежать подобных эффектов, любую файловую систему необходимо перед началом работы с ней в явной форме подключить к ОС (смонтировать - mount), а по окончании отключить (размонтировать - unmount). Для этой цели служат команды mount и umount (без n, хотя соответствующее действие называется unmount). Команда mount имеет множество опций (см. врезку "Монтирование: подробности для любознательных"), но обязательных аргументов у ее стандартного варианта два: имя файла блочного устройства и имя каталога. В результате выполнения этой команды файловая система, расположенная на указанном устройстве, подключается к системе таким образом, что ее содержимое заменяет собой содержимое заданного каталога (поэтому для монтирования обычно используют пустой каталог). Команда umount выполняет обратную операцию - отсоединяет файловую систему, после чего накопитель можно извлечь и положить на полку (на самом деле проблемы возникают почти исключительно с дискетами: CD-ROM, магнитно-оптический диск или Zip-диск, который забыли размонтировать, просто не удастся вытащить без помощи скрепки - он блокируется).
Такие разные файлы
Осталось выяснить, что такое файл блочного устройства. Для этого следует рассказать о том, какие вообще "звери" встречаются в файловой системе. Очевидно, там есть обычные файлы и каталоги, но это далеко не все. Файлами с точки зрения Linux являются также:
- символьные устройства;
- блочные устройства;
- именованные каналы (named pipes);
- гнезда (sockets);
- символьные ссылки (symlinks).
Все эти "звери" в чем-то похожи на обычные файлы, а в чем-то отличаются от них (во всяком случае, все они могут быть удалены системным вызовом unlink, что сближает их с обычными файлами и отдаляет от каталогов). Рассмотрим их по порядку.
Символьные и блочные устройства
Файлы символьных и блочных устройств создаются с помощью программы mknod и соответствуют внешним устройствам, а также псевдоустройствам, таким как знаменитое пустое устройство /dev/null (такое, при попытке чтения из которого сразу же сообщается о достижении конца файла и при записи в которое никогда не происходит переполнения - точный аналог NUL в DOS/Windows).
Устройства нумеруются двумя целыми числами - старшим (major number) и младшим (minor number). Первое из них соответствует типу устройства (например, для устройств, подключенных к первому IDE-контроллеру, оно равно 3, для подключенных ко второму - 22 и т. д.), а второе - конкретному устройству (например, для мастер-диска, подключенного к первому IDE-контроллеру, оно равно 0, для первого раздела на этом диске - 1 и т. д.). При этом символьные и блочные устройства нумеруются независимо.
Число 30Ah в поле "положение" информации о файлах /usr/bin/unzip и /usr/bin/zipinfo на рис. 1 и 2 обозначает как раз номер устройства: 3 - это первый IDE-диск, а 0A - его десятый раздел.
Различие между файлами символьных и блочных устройств заключается в том, что к первым разрешен только последовательный доступ, а вторые допускают обращение (для чтения или записи) к произвольному месту устройства.
Многие устройства имеют дополнительные характеристики: скажем, на консоли (виртуальной) IBM PC есть три лампочки - Num Lock, Caps Lock и Scroll Lock, - а последовательный порт может передавать данные с различной скоростью. Как правило, программы в Linux работают просто с файлами, никак или почти никак не учитывая особенности того или иного конкретного устройства. Если же программа должна их учитывать, она осуществляет управление соответствующими параметрами через системный вызов ioctl, позволяющий, например, зажечь Num Lock или изменить громкость звука. В основном использование ioctl сводится к управлению конфигурацией (отсюда и название - I/O ConTroL, т. е. управление вводом-выводом).
Основной недостаток описанной схемы в том, что она плохо масштабируется: различных устройств существует великое множество, и поскольку всякое устройство, потенциально подключаемое к компьютеру, должно получить номер (а многие - несколько номеров), этих самых номеров явно не хватает. Кроме того, хотя в каталоге /dev, где традиционно хранятся файлы устройств, представлен, разумеется, далеко не весь спектр аппаратуры (только для SCSI-устройств потребовалось бы завести не одну тысячу файлов), он все-таки обычно содержит более тысячи файлов, и лишь очень малая их часть соответствует устройствам (или псевдоустройствам), реально присутствующим в системе.
Эта проблема была осознана достаточно давно, и уже предложено несколько вариантов ее решения. Однако самым распространенным остается пока стандартный подход, когда все файлы устройств создаются вручную программой mknod. Мне представляется наиболее удачным вариант с использованием специальной файловой системы devfs, монтируемой в каталог /dev, в которой специальные файлы устройств создаются и уничтожаются автоматически по мере надобности (именно так настроен компьютер, где сейчас пишутся эти строки).
Именованные каналы
Канал - это простейшее, но очень удобное и широко применяемое средство обмена информацией между процессами. Все, что один процесс помещает в канал (буквально - в "трубу"), другой может оттуда прочитать. Если два процесса, обменивающиеся информацией, порождены одним и тем же родительским процессом (а так чаще всего и происходит), канал может быть неименованным. В противном случае требуется создать именованный канал, что можно сделать с помощью программы mkfifo. При этом собственно файл именованного канала участвует только в инициации обмена данными.
Гнезда
Вообще гнезда (и взаимодействие программ при помощи гнезд) играют очень важную роль во всех Unix-системах, включая и Linux: они являются ключевым понятием TCP/IP и соответственно на них целиком строится Internet. Однако c точки зрения файловой системы гнезда практически неотличимы от именованных каналов: это просто метки, позволяющие связать несколько программ. После того как связь установлена, общение программ происходит без участия файла гнезда: данные передаются ядром ОС непосредственно от одной программы к другой*.
Символические ссылки
Символическая ссылка - относительно новое понятие в Unix. Это особый файл с информацией о том, что требуемый файл в действительности находится в другом месте, и о том, где именно его искать. Например, файл /usr/bin/gzip представляет собой символическую ссылку, указывающую на файл /bin/gzip; благодаря ей можно использовать /bin/gzip, обращаясь к нему как к /usr/bin/gzip. Близким аналогом символических ссылок являются ярлыки Windows 9X и NT 4.0, но ярлыки интерпретируются Проводником Windows, а символические ссылки - непосредственно ядром ОС.
В отличие от обычной, или, как еще говорят во избежание путаницы, жесткой ссылки, символическая ссылка может указывать на файл из другой файловой системы (например, находящийся на другом диске). Заметим, что при создании жесткой ссылки мы получаем два равноправных объекта (при удалении файла /usr/bin/unzip файл /usr/bin/zipinfo будет работать по-прежнему), а вот символическая ссылка при удалении (или переименовании/перемещении) объекта, на который она указывает, "провисает" и становится неработоспособной.
Во второй части статьи мы рассмотрим права доступа к файлам и работу Linux с другими файловыми системами.
* В настоящее время гнезда, создаваемые с помощью соответствующих специальных файлов, так же как и именованные каналы, не позволяют связывать программы, работающие на разных машинах сети.
Монтирование.
Как уже было сказано, монтирование файловых систем выполняется командой mount, а их размонтирование - командой umount. Исключение составляет корневая файловая система*, которая обслуживается отдельно и до всех остальных систем. Действительно, только при ее наличии становятся доступными и сама команда mount, и каталог /dev, где находятся файлы устройств, и подкаталоги для монтирования. Чтобы файловые системы можно было монтировать при запуске ОС и размонтировать при ее остановке, используются два файла, которые традиционно размещаются в подкаталоге /etc: /etc/fstab и /etc/mtab.
Файл /etc/fstab содержит список файловых систем, которые могут быть смонтированы. Конечно, необходимые параметры всегда можно указать при вызове команды mount, но гораздо удобнее, когда они извлекаются из файла. Содержимое моего файла /etc/fstab показано на рис. 3.
Рис. 3. Пример файла fstab
Каждой точке монтирования в нем соответствует одна строка, состоящая из шести полей: название устройства, на котором расположена файловая система, точка монтирования, тип файловой системы, параметры монтирования, "уровень дампа" и порядковый номер файловой системы для программы fsck. Рассмотрим эти поля по порядку.
Название устройства
Чаще всего в этом поле задается имя блочного устройства, на котором размещена файловая система, но так бывает не всегда: для файловой системы procfs, дающей доступ к внутренним структурам ядра, здесь может находиться любой текст (в примере - слово none), для сетевой файловой системы указывается имя сервера и подкаталога на нем и т.д. Даже для обычных файловых систем данное поле иногда содержит нечто отличное от имени устройства: скажем, в трех последних строках моего файла /etc/fstab это имена файлов с образами дисков CD-ROM. Кроме того, разрешается указать вместо имени устройства метку диска или его серийный номер, например:
LABEL=temp /tmp ext2 defaults 1 2 UUID=3a30d6b4-08a5-11d3-91c3-e1fc5550af17 /usr ext2 defaults 1 2
Эта возможность редко применяется на машинах с обычной конфигурацией аппаратуры, но очень полезна в случае, когда к компьютеру подключены десятки дисков через ряд SCSI-контроллеров или интерфейс Firewire, либо когда на нем происходит работа со множеством сменных накопителей и имеется несколько устройств для их чтения.
Точка монтирования
Поле со вполне, казалось бы, очевидным значением, но пара маленьких подвохов все-таки имеется. Во-первых, при автоматическом монтировании файловые системы монтируются в том порядке, в каком они перечислены в /etc/fstab, и если, например, файловая система /home у вас находится на отдельном диске, а /home/ftp - еще на одном диске, то строка с описанием /home должна стоять в /etc/fstab до строки с описанием /home/ftp. Во-вторых, для раздела (или файла) подкачки данное поле никак не используется, и его содержимое может быть любым - лишь бы оно вообще присутствовало (этого требует формат файла).
Тип файловой системы
Уж здесь-то какие могут быть подвохи? Их и нет. Почти. Можно задать в этом поле значение auto, и тогда команда mount попытается сама определить тип файловой системы. Это не так уж замечательно, как может показаться: тип файловой системы определяется путем проверки так называемого "магического числа", которая срабатывает далеко не всегда, а кроме того, перебираются только файловые системы, которые поддерживаются ядром в данный момент (они перечислены в файле /proc/filesystems). Иначе говоря, если у вас имеется дискета с файловой системой minix, а ни одного раздела с этой системой не смонтировано, то при явном задании типа в память будет загружен модуль с файловой системой minix и дискета смонтируется, а при указании типа auto этого, скорее всего, не произойдет и смонтировать дискету не удастся.
Параметры монтирования
Это поле обладает одной весьма неприятной особенностью: часть задаваемых в нем параметров интерпретируется командой mount, а часть - ядром системы. Параметры, интерпретируемые ядром, различны в зависимости от файловой системы и версии ядра (некоторые из них будут рассмотрены в разделе, посвященном "иностранным" файловым системам), а команда mount интерпретирует следующие параметры:
- async - весь ввод-вывод осуществляется асинхронно;
- atime - изменять параметр "время доступа" при обращении к файлам (по умолчанию);
- auto - система может быть смонтирована при автоматическом монтировании;
- defaults - установки по умолчанию: rw + suid + dev + exec + auto + nouser + async;
- dev - система может содержать файлы блочных и символьных устройств;
- exec - она может содержать исполняемые файлы;
- loop - для размещения системы можно использовать обычный файл (стандартно файловые системы размещаются на устройствах, к каковым обычные файлы не относятся, но если указать параметр loop, программа mount находит свободное loop-устройство, "связывает" с ним с помощью программы losetup заданный файл и передает имя этого устройства системному вызову mount; именно так монтируются образы CD-ROM в трех последних строках моего файла fstab);
- noatime, noauto, nodev, noexec, nosuid, nouser - параметры, противоположные по значению соответствующим параметрам без "no-";
- remount - повторно смонтировать уже смонтированную файловую систему (например, чтобы сменить параметры монтирования);
- ro - смонтировать файловую систему только для чтения;
- rw - смонтировать файловую систему для чтения и записи (установка по умолчанию);
- suid - разрешить интерпретацию битов SUID и SGID (подробнее мы рассмотрим их в разделе, посвященном правам доступа);
- sync - весь ввод-вывод осуществляется синхронно;
- user - обычный пользователь (не суперпользователь) наделяется правом монтировать и размонтировать данную файловую систему; этот параметр влечет за собой noexec, nosuid и nodev, если после него явно не указано exec, dev или suid.
"Уровень дампа"
Это поле - самое мистическое в /etc/fstab. Оно используется программой dump, предназначенной для создания резервных копий. Если файловая система должна участвовать в процессе резервного копирования, то здесь должно стоять число 1, если нет - 0 (в действительности возможны и другие значения, но чтобы объяснить их смысл, пришлось бы вникать в тонкости работы программы dump).
Порядковый номер файловой системы для программы fsck
Перед автоматическим монтированием во время загрузки ОС файловая система тестируется программой fsck, которая проверяет ее целостность и, если необходимо, исправляет простейшие ошибки. Чтобы ускорить этот процесс, можно запустить fsck параллельно для нескольких файловых систем. Однако параллельно обрабатываемые системы должны находиться на разных дисках, иначе вместо ускорения обработки мы получим замедление, ибо диск будет непрерывно получать запросы на чтение разных его участков. Значение, установленное в данном поле, позволяет влиять на последовательность проверки: если присвоить файловым системам одинаковые номера, они будут проверяться одновременно. Системы, для которых этот параметр установлен в 0, не проверяются вообще. Для корневой файловой системы его значение несущественно.
Если в файле /etc/fstab имеется строка, относящаяся к данной файловой системе, то при вызове для нее программы mount можно опустить параметры монтирования, название устройства или точку монтирования. Этот файл используется также в графических оболочках, таких как KDE, где монтирование файловой системы сводится к щелчку мышью.
Программа amd и особая файловая система autofs позволяют сделать так, чтобы файловая система автоматически монтировалась при "заходе" в специальный каталог и размонтировалась через некоторое время после того, как последняя программа перестанет использовать файлы из нее. Однако это достаточно рискованно при работе с обычными дисководами гибких дисков: если по ошибке вынуть дискету из дисковода в тот момент, когда она смонтирована, можно серъезно разрушить файловую систему на ней.
В файле /etc/mtab хранится информация о том, какие файловые системы сейчас смонтированы и с какими параметрами монтирования это было сделано. Данные о смонтированных файловых системах содержатся также в файле /proc/mounts (и там они точнее, поскольку отображают соответствующую внутреннюю таблицу ядра), но параметров, с которыми эти системы были смонтированы, в нем нет, поскольку они в ядре не хранятся (а те из них, которые интерпретируются программой mount, вообще не доходят до ядра), поэтому /etc/mtab также находит применение.
Права доступа
Теперь, когда мы выяснили, какие объекты встречаются в файловой системе, полезно вспомнить, что Linux, подобно другим Unix-системам, является многопользовательской ОС, а значит, доступ к файлам должен ограничиваться.
Пользователи и группы
Стандартная система прав доступа в Linux, пришедшая все из тех же далеких 70-х, весьма проста и логична, хотя и не всегда удобна*. Ее базовые понятия — пользователь и группа. Каждый зарегистрированный в системе пользователь получает уникальный номер-идентификатор. Кроме того, он должен входить хотя бы в одну группу, которая является для него «главной», и может входить в другие — «дополнительные» (supplementary groups). Все группы также перенумерованы. Файл всегда связан с определенным пользователем — своим владельцем — и с определенной группой, т. е. у него есть UID (User ID, идентификатор пользователя) и GID (Group ID, идентификатор группы). Изменять права доступа к файлу разрешено только его владельцу. Изменить владельца файла может суперпользователь, группу — суперпользователь или владелец файла (см. рис. 1).
|
Рис. 1. Midnight Commander: изменение владельца и группы файла с помощью команды chown |
Программа, выполняющаяся в системе, всегда запускается от имени какого-то пользователя и какой-то группы (обычно — основной группы этого пользователя), но связь процессов с пользователями и группами организована сложнее: здесь различаются идентификатор для доступа к файловой системе (FSUID — File System access User ID, FSGID — File System access Group ID) и эффективный идентификатор (EUID — Effective User ID, EGID — Effective Group ID), а при доступе к файлам учитываются еще и полномочия (capabilities), присвоенные самому процессу. Соответствующие механизмы мы разберем, когда будем рассматривать управление процессами.
При создании файл получает UID, совпадающий с FSUID процесса, который его создает, и, как правило, GID, совпадающий с FSGID этого процесса; об исключении будет сказано чуть ниже.
Атрибуты доступа
Права доступа приписываются узлу файла. Каждому файлу сопоставлены три набора атрибутов доступа: для владельца, для группы и для всех остальных.
Атрибуты определяют, что разрешено делать с данным файлом данной категории пользователей. Каков же набор возможных операций в Linux (и — шире — в Unix)? Оказывается, их всего три: чтение, запись и выполнение. Как же, спросите вы, быть, например, с созданием и удалением файлов? И как можно выполнить каталог?
При создании файла (или еще одного имени для уже существующего файла) модифицируется не сам файл, а каталог, в котором появляются новые ссылки на узлы. Удаление же файла, как мы помним, заключается в удалении ссылки. Таким образом, право на создание или удаление файла — это право на запись в каталог.
Право на выполнение каталога интерпретируется как право на поиск в нем (прохождение через него). Оно позволяет обратиться к файлу по пути, содержащему данный каталог, даже тогда, когда каталог не разрешено читать и список всех его файлов поэтому недоступен.
Помимо трех названных основных атрибутов доступа существуют дополнительные, используемые в особых случаях. Наиболее важные из них — SUID, SGID и SVTX.
Атрибуты SUID и SGID существенны при запуске программы на выполнение: они требуют, чтобы программа выполнялась не от имени запустившего ее пользователя (группы), а от имени владельца (группы) того файла, в котором она находится. Выражаясь более формально, если файл программы имеет атрибут SUID (SGID), то FSUID и EUID (FSGID и EGID) соответствующего процесса не наследуются от процесса, запустившего его, а совпадают с UID (GID) файла. Благодаря этому «рядовые» пользователи получают, например, возможность запустить системную программу, которая создает свои рабочие файлы в закрытых для них каталогах.
Кроме того, если процесс создает файл в каталоге, имеющем атрибут SGID, то файл получает GID не по FSGID процесса, а по GID каталога. Это удобно для коллективной работы: все файлы и подкаталоги в каталоге автоматически оказываются принадлежащими одной и той же группе, хотя создавать их могут разные пользователи.
Атрибут SVTX (называемый также sticky bit, т. е. бит-липучка) в эпоху молодости Unix определял, нужно ли выгружать программу из памяти по окончании ее выполнения, однако сейчас от ручного управления выгрузкой отказались. Зато SVTX приобрел новое значение применительно к каталогам: из каталога, имеющего этот атрибут, ссылку на файл может удалить только владелец файла. SVTX применяется в первую очередь в каталоге /tmp, где хранятся временные файлы: создать там файл может любой пользователь системы, а удалить — только тот, кто его создал (и, разумеется, суперпользователь).
Запись прав доступа
Существуют две стандартных формы записи прав доступа — символьная и восьмеричная. Символьная представляет собой цепочку из десяти знаков, первый из которых не относится собственно к правам, а обозначает тип файла (‘-’ — обычный файл, ‘d’ — каталог, ‘c’ — символьное устройство, ‘b’ — блочное устройство, ‘p’ — именованный канал, ‘s’ — «гнездо», ‘l’ — символическая ссылка). Далее следуют три последовательности, каждая из трех символов, соответствующие правам пользователя, группы и всех остальных. Наличие права на чтение обозначается буквой ‘r’, на запись — ‘w’ на выполнение — ‘x’, отсутствие какого-либо права — знаком ‘-’ в соответствующей позиции. Наличие атрибута SUID (SGID) обозначается буквой ‘S’ в позиции права на выполнение для владельца (группы), если выполнение не разрешено, и буквой ‘s’, если разрешено. SVTX записывается соответственно буквой ‘T’ или ‘t’ в позиции права на выполнение для «посторонней публики».
Восьмеричная запись — шестизначное число, первые два знака которого обозначают тип файла и довольно часто опускаются, третий — атрибуты GUID (4), SGID (2) и SVTX (1), а оставшиеся три — соответственно права владельца, группы и всех остальных: чтение — 4, запись — 2, выполнение — 1.
Например, стандартный набор прав доступа для каталога /tmp в символьной форме выглядит как drwxrwxrwt, а в восьмеричной как 041777 (это каталог; чтение, запись и поиск разрешены всем; установлен атрибут SVTX). А набор прав -r-S—xw—, или 102412, будет означать, что это обычный файл, владельцу разрешается читать его, но не выполнять и не изменять, пользователям из группы файла (за исключением владельца) — выполнять (причем во время работы программа получит права владельца файла), но не читать и не изменять, а всем остальным — изменять, но не читать и не выполнять. Пример, конечно, условный: трудно вообразить себе ситуацию, в которой действительно потребовалось бы назначить файлу подобные права доступа.
А вот несколько более реалистичный пример. Вообразим себе, что среди пользователей некой машины имеется группа «писателей», которые сочиняют коллективную монографию, и группа «читателей», которые по ходу написания читают ее и высказывают свои замечания. Пользователям, не входящим ни в одну из этих групп, материалы должны быть недоступны. На первый взгляд кажется, что здесь мы натыкаемся на ограниченность системы прав Linux: у файла (или каталога) может быть только один GID.
Однако выход достаточно прост: если сформировать группы так, чтобы список «писателей» был включен в список группы «читателей» (что вполне логично — только в анекдоте чукча может быть писателем, не будучи читателем), то нужного результата можно добиться с помощью иерархии из двух каталогов:
d—-r-x—- /readers UID=0,
GID=<readers>
d—-rwsr-x /readers/writers
UID=0, GID=<writers>
Все файлы размещаются в каталоге /readers/writers. Модифицировать их разрешено «писателям», а читать всем, но реально добраться до этого каталога смогут только те, у кого есть право на выполнение каталога /readers, т. е. «читатели».
Большинство программ создают файлы с разрешением на чтение и запись для всех пользователей, а каталоги — с разрешением на чтение, запись и поиск для всех пользователей. Этот исходный набор атрибутов логически складывается с «пользовательской маской» — user file-creation mask, сокращенно umask, которая обычно ограничивает доступ. Так, для описанного только что примера значение umask должно быть u=rwx,g=rwx,o=rx, т. е. у владельца и группы остается полный набор прав, а всем остальным запрещается запись. В восьмеричном виде оно запишется как 002 (первый знак — ограничения для владельца, второй — для группы, третий — для остальных, запрещение чтения — 4, записи — 2, выполнения — 1).
В дальнейшем владелец файла может изменить права доступа к нему командой chmod, а если он не хочет разбираться с запутанным синтаксисом команды, то с помощью какой-либо из многочисленных программ-оболочек (например, Midnight Commander — см. рис. 2).
|
Рис. 2. Midnight Commander: изменение прав доступа к файлу |
Новые пользователи часто не могут понять, в чем разница между атрибутами «время модификации» (modification time) и «время изменения» (change time). В действительности первое соответствует тому моменту, когда в последний раз изменялось содержимое файла, а второе — тому, когда изменялись его характеристики, например ссылки или права доступа.
Об именах
Вернемся теперь к именам файлов. Как уже говорилось, они не очень важны с точки зрения Linux и вообще Unix: например, существует функция системной библиотеки, которая создает и сразу же «удаляет» файл со случайным именем, после чего возвращает ссылку на него. Ставший безымянным файл продолжает существовать до окончания использования, так что программа получает в свое распоряжение файл, к которому нельзя обычным образом обратиться извне. Однако с точки зрения пользователя имя все-таки представляет собой довольно существенный параметр файла.
Какие же ограничения накладываются на имена файлов? Их немного — поразительно немного для людей, работавших ранее с другими ОС:
- имя файла не должно содержать символов ‘\0’ (символ с кодом 0) и ‘/’;
- имя файла не должно быть длиннее 255 символов;
- полный путь к файлу должен быть не длиннее 4096 символов.
Это все. Причем есть способ добраться и до файла, путь к которому оказался слишком длинным; мы расскажем о нем в связи с процессами.
Имена файлов считаются одинаковыми, если они совпадают побитно, т. е. разница между большими и маленькими буквами существенна. Тем самым, проблема поддержки разных кодировок отпадает: ядру системы неважно, какой набор символов использовался для записи имени — Latin 1, koi8-u, UTF-8 или Big5, лишь бы в нем не встречались два «запрещенных» символа. Иногда это приводит к курьезам. Например, в результате загрузки с помощью программы Minicom файла C:\WINDOWS\ COMMAND\EDIT.COM вполне может образоваться файл с именем из 27 символов — «C:\WINDOWS\ COMMAND\EDIT.COM» (первый символ — ‘C’, второй — ‘:’, третий - ‘\\’ и т. д).
К сожалению, допустимость имени с точки зрения ядра системы еще не гарантирует того, что с ним смогут работать прикладные программы. Среди последних немало даже таких, которые вообще не признают символов с ненулевым старшим битом (а значит, «не понимают» русских букв). В этом смысле русификация Linux является противоположностью русификации DOS: в DOS основной проблемой было «научить» систему вводить и отображать русские буквы (особенно в именах файлов); когда это было сделано, большинство программ начали совершенно свободно работать с кириллицей. В Linux же поддержка русских букв имелась в ядре системы «с самого начала» (их ввод и вывод на системной консоли был обеспечен хотя и не «с самого начала», но также очень и очень давно), а все необходимое для русификации (за исключением, пожалуй, масштабируемых шрифтов) входит в комплект любого современного дистрибутива. Однако программы зачастую отказываются работать с русскими буквами, и их нужно либо специально настраивать (как, например, bash), либо обманывать с помощью «шаманских» приемов (так приходится поступать со StarOffice).
«Чужие» файловые системы
Поговорим теперь немного о поддерживаемых в Linux «чужих» файловых системах. В действительности общих правил работы с ними не существует — слишком уж они разнообразны, — поэтому ограничимся тем, что рассмотрим по отдельности несколько наиболее важных.
Файловые системы Unix
Начать удобнее с систем, которые нельзя считать в полном смысле слова «чужими», поскольку они также применяются в ОС семейства Unix и обслуживаются ядром Linux наравне со стандартной Ext2 fs. Это Minix fs (Minix, Xenix), System V/Coherent fs (System V, Xenix) и UFS (FreeBSD, NetBSD, OpenBSD, SunOS/Solaris, NextStep, OpenStep).
Во всех названных системах (включая, естественно, и Ext2 fs) пользователи и группы представлены только идентификаторами (UID, GID); фактически ядро ОС ничего не знает об их именах. Следовательно, человек, сумевший подключить ваш диск к машине, на которой он является суперпользователем, получит возможность читать и модифицировать любую информацию на диске, не зная пароля суперпользователя вашей системы. (В ряде случаев для этого достаточно быть суперпользователем одной из установленных на машине ОС, причем не обязательно Linux — подойдет даже Windows 95, если только злоумышленнику удастся получить из нее доступ к вашему диску.)
Поэтому обычно не имеет смысла ограничивать права доступа к файлам на сменных носителях информации. Чтобы защитить хранящиеся там данные, лучше их зашифровать. О прозрачном шифровании файлов в Linux можно узнать по адресам http://www.kerneli.org/ и http://tcfs.dia.unisa.it/. В дальнейшем поддержка шифрования, видимо, будет встроена в ядро Linux (в связи с изменением законодательства США, касающегося экспорта криптотехнологий, такая возможность теперь появилась), однако неясно, произойдет ли это уже в версии 2.4.
FAT
Наиболее распространенная файловая система — это, конечно же, FAT (в Linux она называется msdos). Множество пророков множество раз предсказывали ей смерть, и все же ее модификации (VFAT, FAT32) до сих пор служат основной файловой системой в Windows 9x, а для дискет даже Windows NT не предлагает ничего другого. Оригинальная версия FAT, сохранявшаяся практически неизменной от MS-DOS 2.0 до MS-DOS 6.22, крайне проста: вся информация о файле хранится в каталоге, и для доступа к ней используется имя файла, построенное по так называемой «формуле 8+3», т. е. состоящее из собственно имени длиной до 8 символов и расширения длиной до 3 символов, разделенных точкой.
Большие и маленькие буквы в именах файлов не различаются: при всех операциях с файлами используются большие буквы (именно это свойство породило известные проблемы с русскими буквами в именах файлов, продержавшиеся вплоть до появления русифицированной версии Windows 3.1). У каждого файла хранится время последней модификации (с точностью до 2 с) и могут быть установлены атрибуты Read Only (только для чтения), Archive (архивный), Hidden (скрытый) и System (системный).
При монтировании диска с FAT атрибут Read Only отображается в соответствующий атрибут файловой системы Linux, остальные же игнорируются, поскольку не имеют аналога. В результате файлы с атрибутом Read Only получают набор прав доступа r-xr-xr-x, а все прочие — rwxrwxrwx. Чтобы как-то еще ограничить права доступа, следует задать среди параметров монтирования нужное значение umask, а чтобы при этом предоставить привилегии определенным пользователям, — указать соответствующие UID и GID. Можно также просто разрешить пользователям самим монтировать разделы, но это не очень удобно, поскольку нельзя смонтировать один и тот же раздел дважды.
VFAT и FAT32
Файловая система VFAT впервые появилась в Windows NT, а широкое распространение получила после выхода Windows 95: это усовершенствованная версия FAT, в которой разрешены длинные имена файлов. FAT32, введенная в Windows 95 OSR2 и поддерживаемая в Windows 98, отличается от VFAT лишь количественными параметрами: она допускает меньший размер кластеров и больший размер дисков, не ограничивает число файлов в корневом каталоге и т. д. Поэтому в Linux работа с VFAT и FAT32 происходит совершенно одинаково; для FAT32 нет даже отдельного драйвера. С этим связан забавный момент: RedHat Linux 5.1 поддерживает FAT32, но единственный способ узнать об этом — попытаться смонтировать соответствующий раздел и убедиться, что он монтируется. В документации FAT32 не упоминается.
Появившиеся в VFAT длинные имена сделали работу с файлами более удобной, однако породили ряд проблем. Во-первых, VFAT сохраняет в именах разницу между большими и маленькими буквами, но для доступа к файлам разрешает использовать любые их комбинации. Во-вторых, что более существенно, у каждого файла в VFAT есть два имени — длинное и короткое. При просмотре диска Linux показывает в смонтированном разделе VFAT только длинные имена, но по короткому имени доступ к файлу также будет предоставлен, а при попытке создать новый файл, имя которого совпадет с коротким именем существующего файла, вы получите сообщение о том, что такой файл уже есть, — точно так же, как и в Windows.
Далее, если в VFAT дать файлу имя, удовлетворяющее ограничениям FAT и состоящее из символов стандартного набора ASCII, то Windows 95 и NT будут считать, что он имеет только короткое имя. При этом с точки зрения Windows 95 такое имя будет состоять только из больших букв, а с точки зрения Windows NT — только из маленьких. Linux здесь выбирает строну NT (по историческим причинам). В большинстве случаев все это не имеет значения; сложности возникают лишь при работе с программой установки RedHat Linux и ее производными, которые отличаются повышенной чувствительностью к названиям каталогов с исходными файлами.
И, наконец, длинные имена файлов записываются в кодировке Unicode. Поэтому при монтировании разделов с VFAT необходимо задавать правила их преобразования, что делается с помощью параметра iocharset; в наших условиях обычно указывают iocharset=koi8-r. Если длинное имя содержит символы, не имеющие соответствия в текущем iocharset, к файлу обратиться невозможно. Чтобы получить доступ ко всем файлам (правда, имена их примут неудобочитаемый вид), нужно вместо iocharset указать uni_xlate= true или utf8= true (подробнее см. /usr/src/linux/Documentation/filesystems/vfat.txt).
Набор символов для коротких имен задается параметром codepage. Когда Windows настроена на русский язык, для коротких имен применяется CP 866 (кодировка DOS), поэтому следует указать codepage=cp866.
ISO-9660
ISO-9660 используется на CD-ROM**. Ее исходная версия не поддерживает прав доступа и символических ссылок; для имен файлов допустима длина до 32 символов, причем большие и маленькие буквы не различаются. Кроме того, если имя файла не укладывается в схему 8+3, то файл не будет доступен из MS-DOS, а если оно состоит из маленьких букв (некоторые программы позволяют так сделать), то и из Windows NT.
Ограничения ISO-9660 преодолеваются в ее расширенном варианте, который называется Rock Ridge. С ним работают различные Unix-системы, включая Linux, но ни в одной из версий Windows он не поддерживается. Зато для Windows 95 разработано альтернативное расширение — Joliet, обеспечивающее только поддержку длинных имен файлов (в формате Unicode); с ним Linux также умеет работать.
При монтировании CD-ROM разрешается отключить Rock Ridge (Joliet) и, если не используется RockRidge, указать UID, GID и umask. Для дисков с Joliet задается iocharset; можно указать и utf8, но не uni_xlate. Все эти параметры имеют то же значение, что и для (V)FAT.
NTFS
О работе с NTFS, к сожалению, можно сказать мало утешительного. Это исключительно сложная и гибкая файловая система, а открытая документация по ней практически отсутствует. Два названных свойства и определяют границы поддержки NTFS в Linux. Обеспечивается работа с версиями вплоть до NTFS4 (Windows NT 4.0). Версия же NTFS5 (Windows 2000 Pro) не поддерживается, и неизвестно, когда появится ее поддержка (если появится вообще).
Даже для тех версий, с которыми Linux работает, доступ предоставляется только к основной секции файла: в NTFS файл может иметь произвольное число секций, аналогичных «вилкам» (forks) MacOS (но в MacOS у каждого файла ровно две секции — «вилка данных» и «вилка ресурсов»). А запись в раздел NTFS возможна только в особом экспериментальном режиме, перед включением которого рекомендуется подготовиться к восстановлению диска после полной потери данных, — и это не просто громкое предупреждение.
Вся система прав доступа NTFS в Linux игнорируется, и доступ к файлам регулируется так же, как для FAT. Правда, если указать параметр posix=yes, можно будет увидеть все имена файлов — и короткие, и длинные. Заодно это позволит увидеть файлы с именами, отличающимися только регистром символов. Откуда они берутся? Дело в том, что в Windows NT есть подсистема POSIX, внутри которой различаются большие и маленькие буквы, есть возможность создавать жесткие ссылки и т. д. Она иногда используется при переносе программ из Unix в Windows NT.
В Linux поддерживается еще много файловых систем (HFS, HPFS и др.), но они реже встречаются на практике. Сетевые файловые системы — coda, smbfs, nсpfs и nfs.
Ниже приведены характеристики и особенности некоторых современных файловых систем, поддерживаемых Linux.
Таблица 1. Некоторые современные файловые системы
Название | Максимальный размер файловой системы | Размеры блоков | Максимальный размер файла |
XFS | 18 тыс. Пбайт | от 512 байт до 64 Кбайт | 9 тыс. Пбайт |
JFS | блок на 512 байт/4 Пбайт | 512/1024/ 2048/4096 байт | 512 Тбайт |
блок на 4 Кбайт32 Пбайт | 4 Пбайт | ||
ReiserFS | 4 гигаблоков | До 64 Кбайт (сейчас фиксированы 4 Кбайт) | 4 Гбайт |
ext3fs | 4 Тбайт | 1 Кбайт - 4 Кбайт | 2 Гбайт |
Таблица 2. Особенности современных файловых систем
Метод/ Файловая система | Управление свободными блоками | Экстенты для свободного пространства | B-деревья для элементов каталогов | B-деревья для адресации блоков файлов | Экстенты для адресации блоков файлов | Данные внутри inode (небольшие файлы) | Данные симво-льных ссылок внутри inode | Элементы каталогов внутри inode (неболь-шие каталоги) |
XFS | B-деревья, индексированные по смещению и по размеру | Да | Да | Да | Да | Да | Да | Да |
JFS | Дерево+ Binary Buddy (1) | Нет | Да | Да | Да | Нет | Да | До 8 |
ReiserFS (2) | На основе битовой карты | Пока не поддерживается | Как поддерево основного дерева файловой системы | Внутри основного дерева файловой системы | Будет реализовано в версии 4 | (3) | (3) | (3) |
ext3fs | ext3fs не является файловой системой, созданной с нуля; она базируется на ext2fs, поэтому не поддерживает ни один из перечисленных выше методов; ext3fs предоставляет ext2fs поддержку журналирования, в то же время сохраняя обратную совместимость. |
Примечания
- JFS использует иной подход к организации дерева блоков. Структура представляет собой дерево, где концевые узлы являются фрагментами битовой карты, а не непрерывными областями. Binary Buddy - это метод, используемый для управления и объединения вместе последовательных групп свободных логических блоков с целью формирования группы большего размера. Как уже было отмечено при обсуждении метода битовой карты, каждый бит в карте соответствует логическому блоку на диске. Значение бита "1" означает, что блок занят, "0" - свободен. Фрагменты битовой карты, каждый из которых содержит 32 бита, могут быть интерпретированы как шестнадцатеричные числа. Так, значение "FFFFFFFF" указывает, что блоки, соответствующие битам этого фрагмента битовой карты, все заняты. Наконец, за счет использования этого числа резервирования и другой информации JFS создает дерево, в котором можно быстро найти группу последовательных блоков определенного размера.
- Эта файловая система базируется на B*деревьях (усовершенствованная версия B+дерева). Основное различие сводится к тому, что каждый объект файловой системы размещается внутри общего B*дерева. Это значит, что для каждого каталога не создается своего дерева, но каждый каталог имеет поддерево в основном дереве файловой системы. Такой подход предполагает, что в ReiserFS применяются более сложные методы индексирования. Еще одно важное отличие состоит в том, что ReiserFS не использует экстенты, хотя в перспективе они будут поддерживаться.
- ReiserFS помещает каждый объект файловой системы внутри B*дерева. Эти объекты, каталоги, блоки файлов, атрибуты файлов, ссылки и так далее - все поддерживаются внутри одного и того же дерева. Для получения значения ключевого поля, необходимого для организации элементов внутри B-дерева, используются методы хеширования. Их несомненным достоинством является тот факт, что за счет изменения применяемого метода хеширования можно изменить способ, каким файловая система организует элементы и их относительное положение внутри дерева.
Журнальные файловые системы (journal file system).
За последние годы Linux приобрела немало новых возможностей и применяется во многих гетерогенных средах. Linux работает на микроконтроллерах, применяется в маршрутизаторах, служит для поддержки аппаратных ускорителей трехмерной графики, поддерживает многоэкранную среду Xfree. Все это важные функции для конечных пользователей. Но немало сделано и для удовлетворения требований, предъявляемых к серверам, в особенности, с момента перехода на ядро Linux 2.2.x.
Благодаря широкой поддержке отрасли и усилиям, предпринимаемым сторонниками свободно распространяемых программ, Linux приобрела важные черты, присущие коммерческим версиям Unix и других ОС для крупных серверов. Одна из таких черт — поддержка файловых систем, способных работать с большими разделами жестких дисков, легко масштабироваться на многие тысячи файлов, быстро восстанавливаться после сбоя, поддерживать более высокую производительность ввода/вывода, эффективно работать с файлами самого разного размера, противостоять внутренней и внешней фрагментации и даже предоставлять новые функции, которые не поддерживаются ни в одной из более традиционных файловых систем.
В этом разделе мы познакомимся с так называемыми журнальными файловыми системами (journal file system): JFS , Ext3 и ReiserFS .
Глоссарий
Внутренняя фрагментация
Логический блок — минимальная единица дискового пространства, которую резервирует файловая система посредством системных вызовов. Если размер файла меньше числа байт в логическом блоке, то на диске он все равно будет занимать один блок. Таким образом, если длина конкретного файла не делится нацело на число байт в логическом блоке (размер файла MOD размер блока є0), то файловая система будет вынуждена резервировать новый блок, который останется незаполненным до конца, что приводит к нерациональному использованию дискового пространства. Такая излишняя трата пространства на диске называется внутренней фрагментацией. Чем больше размер логического блока, тем больше будет и внутренняя фрагментация.
Внешняя фрагментация
Внешняя фрагментация возникает в ситуации, когда логические блоки конкретного файла разбросаны по всему диску, что приводит к замедлению операций с данным файлом, поскольку требует большего числа движений считывающей головки диска.
Экстент
Экстент (непрерывная область на диске) представляет собой последовательность логических блоков. Механизм экстентов используется несколькими файловыми системами и ядрами баз данных. Описатель экстента обычно состоит из трех полей: beginning, extent size, offset, где beginning («начало») — это адрес блока, с которого начинается экстент, extent size («размер экстента») — это количество блоков, а offset («сдвиг») — это смещение первого байта экстента относительно начала файла.
Экстент позволяет более эффективно расходовать дисковое пространство, поскольку все блоки в нем располагаются последовательно. Кроме того, при чтении файла выполняется меньше движений считывающей головки. Использование экстентов сокращает негативный эффект внешней фрагментации, поскольку большее число блоков хранится последовательно. В том случае, если приложению необходим экстент, близкий по размеру к логическому блоку, преимущества от его применения сводятся к минимуму, поскольку в итоге создается множество небольших экстентов, которые будут играть роль обычных логических блоков. Рост производительности при использовании экстентов достигается за счет уменьшения перемещений между секторами и сокращения числа промахов при обращении к кэш-памяти диска.
Экстенты позволяют эффективно организовывать большие цепочки свободных, последовательно расположенных блоков. Они помогают сократить объем дискового пространства, требуемый для отслеживания свободных блоков, позволяя тем самым увеличить производительность.
B+деревья
B+деревья широко используются в индексных структурах баз данных, обеспечивая быстрый и масштабируемый доступ к записям. Название «B+дерево» — сокращение от Balanced Tree («сбалансированное дерево»). Знак «+» указывает на то, что это модифицированная версия «исходного» B-дерева; она содержит указатели, которые провязывают между собой листья дерева, что помогает последовательному доступу.
B+деревья состоят из узлов двух различных типов: промежуточные узлы и концевые узлы (листья). Все узлы содержат в себе множества пар (ключ, указатель), упорядоченные по значению ключа в порядке возрастания. Указатели промежуточных узлов служат для ссылки на другие указатели промежуточных или концевых узлов, указатели концевых узлов — непосредственно на информацию. Ключ применяется для организации информации внутри B+дерева. В базах данных каждая запись имеет поле ключа, по значению которого различают записи одного и того же типа. B-деревья используют ключ для построения индекса записей баз данных, который позволяет сократить время доступа к ним. Сравнивая текущий ключ с ключом искомого узла, программа находит требуемую информацию.
Чтобы найти определенную запись, необходимо сделать следующее. Предположим, что мы ищем запись с определенным ключом — «K». Поиск следует начинать с корневого узла, а затем последовательно сканировать ключи, хранящиеся внутри. Узел сканируется до тех пор, пока не найден ключ со значением больше «K». Затем переходим к узлу (промежуточному или конечному, пока не известно точно, к какому), на который ссылается соответствующий указатель. После этого, если этот узел является промежуточным, операция повторяется. Наконец, найден конечный узел, который последовательно сканируется до тех пор, пока не найден требуемый ключ «K». Поскольку, для того чтобы найти нужный ключ, требуется извлечь меньшее число блоков, этот метод поиска имеет меньший уровень сложности, чем последовательное сканирование, при котором, в самом худшем случае, пришлось бы просмотреть все элементы.
UNIX File System
UFS — название файловой системы, использовавшейся в SCO Unix, System V и некоторых других ранних вариантах Unix. Ядро Linux включает факультативную поддержку UFS. Большинство диалектов Unix по-прежнему использует UFS, хотя и с небольшими специфическими модификациями.
Virtual File System
VFS — специальный уровень в ядре, который предоставляет унифицированный API-интерфейс файловых служб, не зависящий от того, в какой именно файловой системе размещается файл. Все реализации файловой системы (vfat, ext2fs, jfs и т.д.) должны предоставлять определенные функции VFS, чтобы их можно было использовать в Linux. Этот уровень абстракции дает пользовательским приложениям возможность работать с множеством различных файловых систем, в том числе, и с коммерчески распространяемыми продуктами.
Журнал
Что такое журнальная файловая система
Общеизвестно, что представляет собой кэш-память — буфер, зарезервированный в быстрой памяти и предназначенный для ускорения операций ввода/вывода. Такого рода буфер часто используется в файловых системах (его называют дисковым кэшем) и в базах данных для увеличения общей производительности. Проблема возникает в том случае, если в системе произошел сбой прежде, чем буферы были записаны на диск; в таком случае после перезагрузки системы она будет находиться в противоречивом состоянии. Представьте себе, что файл был удален из кэша, но остался на жестком диске. Вот почему базы данных и файловые системы должны иметь возможность возвращать систему в непротиворечивое состояние. За долгие годы совершенствования баз данных были созданы способы, позволяющие быстро восстанавливать их, однако, время восстановления файловых систем и, более конкретно, UFS-подобных систем увеличивается по мере роста размеров файловой системы. Утилита восстановления fsck для ext2fs должна просканировать раздел диска целиком, чтобы вернуть файловую систему обратно в непротиворечивое состояние. Из-за того, что эта задача требует очень больших затрат времени в случае крупных серверов с сотнями гигабайт, а иногда и терабайт данных, говорить о высоком уровне готовности подобных систем не приходится. Именно это стало основной причиной создания для файловых систем технологии, подобной технологии восстановления баз данных, в силу чего и появились журнальные файловые системы.
Механизм работы
Транзакция представляет собой набор простейших операций, удовлетворяющий определенным свойствам; речь идет о так называемых свойствах ACID (Atomicity, Consistency, Isolation, Durability — «атомарность, целостность, изолированность, долговечность»). Самое важное свойство в контексте данной статьи — атомарность. Данная характеристика означает, что все относящиеся к одной транзакции операции выполняются полностью, либо отменяются, не порождая никаких изменений. Эти свойства транзакций создают основу функционирования баз данных, беря на себя решение проблем, связанных с необходимостью сохранения целостности при параллельной работе с ними. Чтобы добиться этого, базы данных записывают в файл регистрации (log file) каждую отдельную операцию транзакции. При этом перед выполнением операции записывается не только ее название, но и значения аргументов. Каждую транзакцию должна завершать операция ее принятия, или подтверждения (commit), в частности, свидетельствующая о том, что буферы записаны на диск. Если в системе произошел сбой, можно просмотреть назад записи в файле регистрации до тех пор, пока не встретится последняя операция принятия транзакции, и постараться восстановить состояние диска на этот момент.
Журнальные файловые системы используют ту же методику для регистрации операций файловой системы, благодаря чему файловую систему можно восстановить за очень небольшой промежуток времени.
Важное различие между базами данных и журналированием файловых систем состоит в том, что базы данных регистрируют пользователей и управляющие данные, в то время как в файловых системах регистрация, как правило, ограничивается только метаданными, т.е. ее внутренними управляющими структурами (описатели inode, таблицы резервирования свободных блоков, таблицы inode и т.д.).
Решение проблемы масштабируемости.
UFS и ext2fs создавались во времена, когда жесткие диски и другие носители не отличались большой емкостью. Рост емкости привел к появлению файлов, каталогов и разделов большего размера, что, в свою очередь, породило ряд серьезных проблем, связанных с файловой системой. Эти проблемы — следствие ограничений внутренних структур, которые используются при построении файловых систем. Такие структуры были вполне адекватны для среднего размера старых файлов и каталогов, однако, в новых условиях они стали неэффективны. Старые структуры порождают две серьезные проблемы.
- Они не в состоянии поддерживать возросшие объемы хранения. Старые файловые системы были рассчитаны на определенные размеры файлов, каталогов и разделов. Структуры файловой системы выделяли определенное число разрядов под данные о размере файла, фиксированное число разрядов для хранения номера логического блока и т.д. Как следствие жестких ограничений на число разрядов для поля размера налагались определенные ограничения и на размер файлов и разделов, а также на количество элементов в каталоге.
- Они не пригодны для управления новыми объемами хранения. Хотя старые структуры иногда способны поддерживать новые размеры объектов, на практике зачастую это невозможно из соображений производительности. Некоторые структуры прекрасно работают со старыми размерами, но в новой ситуации часто становятся причиной резкого падения производительности.
Файловые системы последнего поколения создавались с учетом необходимости решить проблемы масштабируемости. В них реализованы некоторые новые структуры и методы.
В большинстве новых файловых систем увеличено число разрядов, отводимых под хранение некоторых полей с тем, чтобы преодолеть существующие ранее ограничения.
В настоящее время максимальный размер блочного устройства ограничивает размер файловой системы 2 Тбайт. Кроме того, в VFS существует ограничение на размер файла в 2 Гбайт. Однако важно отметить, что теперь файловые системы могут масштабироваться, и с выпуском ядра Linux 2.4 эти ограничения преодолены. JFS и XFS — коммерческие версии файловых систем, предназначенные для тех ОС, в которых подобных ограничений нет.
Усовершенствования
Структура свободных блоков
Большинство файловых систем поддерживают структуры, в которых отслеживаются свободные блоки и которые, как правило, содержат список всех номеров свободных блоков. Благодаря таким структурам файловая система в состоянии удовлетворить запросы, выдаваемые приложениями. Для контроля свободных блоков в UFS и ext2fs применяются так называемые битовые карты. Битовая карта состоит из массива бит, каждый бит которого соответствует логическому блоку в разделе файловой системы. Состояние резервирования каждого блока будет отражаться соответствующим битом. Логическое значение «1» может означать, что блок занят, а «0» — свободен. Основная проблема, возникающая при использовании такой структуры, состоит в том, что битовая карта увеличивается в размерах параллельно с ростом размеров файловой системы, поскольку каждому отдельному блоку в файловой системе должен соответствовать собственный бит в битовой карте. До тех пор, пока применяется алгоритм последовательного поиска свободных блоков, производительность будет падать, поскольку время, необходимое для поиска свободного блока, также будет расти (в худшем случае сложность составит O(n), где n — размер битовой карты). Подход, предусматривающий применение битовой карты, себя вполне оправдывает при файловой системе среднего размера, но по мере ее роста ситуация ухудшается.
Решение, предлагаемое в файловых системах нового поколения, предусматривает использование экстентов с одновременным построением B+дерева. Этот подход оправдан, поскольку может одновременно применяться для определения местонахождения свободных блоков. Кроме того, работа с экстентами дает возможность сократить размер структуры: не нужно ставить бит в соответствие каждому отдельному блоку. Более того, при использовании экстентов размер структуры свободных блоков зависит не от размера файловой системы, а от допустимого числа экстентов. Производительность также должна возрасти, поскольку при поиске будет обрабатываться меньшее число элементов. Кроме того, при работе с экстентами, даже в том случае, если применяются список и алгоритмы последовательного поиска, производительность все равно возрастет, поскольку структура будет объединять несколько блоков в одну непрерывную область, и, тем самым, будет сокращаться время поиска запрашиваемого числа свободных блоков.
Второй подход к решению проблемы управления свободными блоками состоит в использовании дополнительных структур, которые позволяют применять алгоритмы поиска с меньшей скоростью роста сложности. B+деревья используются именно потому, что они помогают быстрее определять местонахождение требуемых объектов. Свободные блоки организуются в B+деревья, а не в списки. Кроме того, применяется и подход, при котором B+деревья описывают не блоки, а экстенты. Это дает возможность применять различные методы индексации. Индексация по размеру или по позиции экстента позволяет файловой системе быстро находить порцию из нескольких свободных блоков, либо по размеру, либо по их местоположению.
Большое число элементов каталога
Все файловые системы используют специальные объекты, называемые каталогами. Элементы каталога суть пары (номер inode, имя файла), где inode — описатель (внутренняя структура файловой системы), используемый для хранения служебной информации, связанной с файлом. Когда приложению требуется определенный файл в каталоге, заданный именем, необходимо просмотреть структуру элементов каталога. Старые файловые системы организовывали элементы в каталоге в виде списка, к которому затем применялись последовательные алгоритмы поиска. Как следствие, при работе с большими каталогами, содержащими тысячи файлов и другие каталоги, производительность оказывалась чрезвычайно низкой. Эта проблема, как и в случае со свободными блоками, связана с использованием соответствующей структуры. Новое поколение файловых систем обладает более эффективными структурами и алгоритмами для определения местонахождения файлов внутри каталога, которые работают намного быстрее.
Файловые системы, о которых говорится в данной статье, для организации элементов внутри каталога используют B+деревья, что позволяет значительно сократить время поиска. Индексация в B+дереве ведется по имени элемента каталога. Таким образом, когда необходим определенный файл в данном каталоге, поиск ведется по B+дереву данного каталога, что позволяет быстро найти inode требуемого файла. Детали использования B+деревьев зависят от конкретной файловой системы. Одни файловые системы поддерживают B+дерево для каждого отдельного каталога, в других существует единое B+дерево для всей иерархии каталогов файловой системы.
Файлы большого размера
Прежде файловые системы создавались в расчете на совершенно определенные методы работы с файлами. ext2fs и UFS были спроектированы с учетом того, что файловая система содержит, главным образом, файлы небольшого размера. Именно этим объясняется структура индексных описателей файлов inode в ext2fs и UFS.
Коротко говоря, описатель inode представляет собой структуру, применяемую в UFS и ext2fs для хранения информации о файле. В inode содержатся данные о правах доступа к файлу, тип файла, число ссылок и указатели на блоки файловой системы, занятые файлом. Описатель включает в себя несколько прямых указателей, которые представляют собой указатели (адреса блоков) на логические блоки файловой системы, используемые файлом, которому они принадлежат; кроме того, он также содержит косвенные указатели, дважды и даже трижды косвенные указатели. Косвенные указатели — это указатели (адреса) на блоки, где хранятся другие указатели на логические блоки. Дважды косвенные указатели — это указатели на блоки, которые содержат косвенные указатели, а трижды косвенные указатели — на блоки с дважды косвенными указателями. Основная проблема, связанная с такой методикой адресации, состоит в том, что по мере увеличения размера файла все более интенсивно используются косвенные указатели всех видов. Следует подчеркнуть, что применение косвенных указателей приводит к увеличению числа обращений к диску: для того чтобы получить требуемый блок, необходимо извлечь больше промежуточных блоков. Таким образом, при росте размера файла увеличивается и длительность операций над ним. Резонен вопрос: почему разработчики ext2fs не ограничились только прямыми указателями, если с ними система работает заведомо быстрее? Основная причина состоит в том, что описатели inode имеют фиксированный размер, и при использовании только прямых указателей этот размер был бы очень велик; в силу этого при работе с небольшими файлами пространство тратилось бы впустую.
Новые файловые системы эффективно используют дисковое пространство, но при этом предлагают лучшую технологию адресации для файлов большего размера. И опять-таки, их основное отличие состоит в использовании B+дерева для организации блоков файлов. Блоки индексируются по смещению в файле; затем, когда требуется определенное смещение, процедуры файловой системы просматривают B+дерево в поисках затребованного блока.
Для того чтобы минимизировать использование косвенных указателей, можно использовать логические блоки большего размера. Однако логические блоки большего размера увеличивают уровень внутренней фрагментации, поэтому применяются другие методы. Один из таких методов — использование экстентов для объединения нескольких логических блоков. Применение экстентов имеет тот же эффект, что и блоки большего размера, поскольку увеличивается соотношение «информация на адресуемый элемент». Некоторые из описываемых файловых систем используют экстенты для решения задачи адресации файлов большого размера. Более того, экстенты можно организовать в B+дерево, индексируя их по смещению в файле, что сокращает время сканирования. Новые описатели inode, как правило, поддерживают определенные прямые указатели на непрерывные области, а в том случае, если файлу требуются дополнительные непрерывные области, они могут быть организованы в B+дереве. Чтобы обеспечить высокую производительность при доступе к небольшим файлам, файловые системы нового поколения хранят данные о файлах внутри самого описателя. Таким образом, когда пользователь получает описатель inode, он также получает и данные о нем. Особенно полезна эта технология для символьных ссылок, когда данных в файле действительно очень немного.
Другие усовершенствования
Для UFS-подобных файловых систем характерно несколько ограничений. Среди них — невозможность управлять неплотными файлами как особым случаем и проблема фиксированного числа описателей inode.
Поддержка разреженных файлов
Предположим, создан новый файл, и в его начало записано пара байт. Что произойдет, если в этот файл записать данные со смещением «10000»? Теперь файловая система должна проанализировать, сколько блоков необходимо, чтобы покрыть разрыв между смещением 2 и смещением 10000. Это может потребовать некоторого времени. Теперь вопрос состоит в следующем: почему файловая система должна резервировать блоки, находящиеся в середине, если нам они не нужны? Ответ на этот вопрос — поддержка разреженных файлов, предлагаемая файловыми системами нового поколения.
Поддержка разреженных файлов тесно связана с методом адресации экстентов областей для блоков файла. Соответствующий механизм использует поле «смещение внутри файла» описателя экстента. Таким образом, когда файловая система вынуждена искать свободные блоки, чтобы заполнить разрыв в такой ситуации, она просто определяет новый экстент с соответствующим значением поля «смещение внутри файла». Впоследствии, когда приложение пытается прочитать байт внутри разрыва, возвращается значение null, поскольку в этом «промежутке» нет никакой информации.
Решение проблемы внутренней фрагментации
При обсуждении внутренней фрагментации и производительности указывалось, что системным администраторам часто приходится выбирать между производительностью и неэффективными затратами пространства. Если вернутся к первой таблице, то становится ясно, что новые файловые системы могут управлять блоками размером до 64 Кбайт. Такой (и даже меньший) размер блока может породить значительные неэффективные затраты пространства из-за внутренней фрагментации. Чтобы использование блоков больших размеров было оправдано, в ReiserFS реализован метод, позволяющий решить данную задачу.
Как отмечалось, ReiserFS использует B*дерево для организации объектов файловой системы. Эти объекты представляют собой структуры, в которых хранится информация о файле — время доступа, права доступа и т.д. Другими словами, информация, содержащаяся в описателях inode, в данных каталогов и файлов. В терминах ReiserFS эти объекты называются «статистическими элементами» (stat_data), «элементами каталогов» и «прямыми/косвенными элементами» соответственно. Косвенные элементы состоят из указателей на неотформатированные узлы (логические блоки без заданного формата, применяемые для хранения данных файла), а прямые элементы содержат сами данные файла. Кроме того, эти элементы имеют разный размер и хранятся в концевых узлах дерева, иногда с другими элементами, в том случае, когда внутри узла достаточно места. Информация о файле хранится по возможности близко к данным файла: файловая система всегда пытается объединить статические элементы с прямыми/косвенными элементами того же файла. Следует подчеркнуть, что, в отличие от прямых элементов, данные файла, на которые указывают косвенные элементы, не хранятся внутри дерева. Для поддержки файлов небольшого размера разработан специальный механизм управления прямыми элементами.
Прямые элементы призваны хранить данные небольших файлов и даже «хвосты» файлов. В силу этого, некоторые хвосты могут храниться внутри одного и того же концевого узла, позволяя значительно сократить уровень неэффективного использования пространства. Однако использование этого метода хранения вместе хвостов файлов увеличивает внешнюю фрагментацию; кроме того, формирование дополнительных структур требует значительных затрат времени и снижает производительность. Поэтому совместное хранение хвостов файлов может быть отключено, если того явным образом потребует системный администратор.
Динамическое выделение inode
Еще одна важная проблема, связанная с UFS-подобными файловыми системами, состоит в том, что они поддерживают фиксированное число описателей inode —максимум описателей определяет максимальное число объектов, которые могут поддерживаться в файловой системе. В том случае, если все inode в файловой системе задействованы, приходится делать резервную копию раздела, а затем переформатировать ее с большим числом inode. Фиксированное число inode определяется потому, что для управления состоянием описателей файлов в UFS используются структуры фиксированного размера — точно так же, как и в случае свободных блоков. Кроме того, UFS резервирует описатели inode для файловой системы в известном месте на диске, поэтому нет необходимости указывать соответствие описателей и логических блоков. Проблема возникает в том случае, когда системному администратору приходится заранее определять максимальное число объектов, которым предстоит управлять файловой системе. Далеко не всегда целесообразно указывать в качестве верхней границы слишком большое число, поскольку для размещения inode резервируется значительное дисковое пространство, которое нельзя использовать для других целей и которое в таком случае будет тратиться впустую.
Возможным решением является динамическое выделение inode. Этот подход позволяет отказаться от назначения максимально возможного числа объектов в момент форматирования файловой системы. Однако использование этого метода выделения описателей порождает другие проблемы: необходимость определять соответствие описателей и логических блоков, поддерживать сложные структуры управления inode и т.д. В описываемых в статье файловых системах для организации динамического выделения inode также используются B+деревья. Более того, в JFS применяются «непрерывные inode», которые формируют концевые узлы в B+дереве, и могут хранить вместе до 32 inode. Использование динамического выделения inode — процедура сложная и требующая больших временных затрат, но она помогает преодолеть ограничения, накладываемые старыми файловыми системами.
Литература
[1] Обзор JFS и технические комментарии Стива Беста и Дейва Клейкампа
[2] Mike Holton, Raj Das, «XFS: A Next Generation Journalled 64-Bit Filesystem With Guaranteed Rate I/O», SGI
[3] Adam Sweeney, Doug Doucette, Wei Hu, Curtis Anderson, Mike Nishimoto, Geoff Peck, «Scalability in the XFS File System», SGI
[4] Philip Trautman, Jim Mostek, «Scalability and Performance in Modern File Systems», см. Web-сайт ReiserFS
Виктор Хименко
15.02.2000
Мир ПК, #02/2000
Хуан Сантос Флоридо
26.10.2001
Открытые системы, #09/2001