Файловая система BSD-семейства
Для файловой системы BSD-систем, начиная с 4.2BSD, используется общее родовое наименование - FFS (Fast File System). Название это отражает их большее быстродействие относительно первознанной s5fs, хотя среди современных файловых систем они - в числе самых медленных. Реализация UFS для FreeBSD и DragonFlyBSD носит название UFS (Unix File System). Во FreeBSD 5-й ветке используется усовершенствованная, 64-разрядная, разновидность последней - UFS2. Она с некоторых пор поддерживается также в NetBSD и DragonFly, хотя и не является в них используемой по умолчанию. Фактически FFS и разновидности UFS - единственные нативные файловые системы для ОС BSD-семейства, хотя на уровне обмена данными в них можно использовать файловые системы FAT-семейства и, с оговорками, некоторые типы файловых систем Linux.
Как уже говорилось ранее, дисковый раздел, на котором создается файловая системы, - это, говоря метафорически, группа смежных цилиндров, разбитых на физические блоки по 512 байт. Однако специфика файловых систем BSD-клана заключается в том, что при их создании раздел делится еще и на части фиксированного объема (зависящего от объема раздела). Это так называемые группы цилиндров, каждая из которых имеет включает три самостоятельные части:
область блоков данных.
Рассмотрение их устройства целесообразно начать с конца списка, то есть с области данных.
Область данных (она занимает подавляющий объем пространства каждой группы цилиндров) образована блоками файловой системы. Подобно физическому блоку, о котором речь была ранее, блок файловой системы (именуемый также блоком логическим) представляет собой квант информации, доступный за одну операцию чтения/записи, но уже не дисковой головки, а операционной системы. Очевидно, что логический блок не может быть меньше блока физического (то есть 512 байт), и размер его обязательно кратен целому числу последних.
Понятие логического блока введено для повышения производительности дисковых операций. Не требует доказательства утверждение, что скорость обмена данными квантами по 1 Кбайт будет выше, чем 512-байтными, 2-килобайтными - еще быстрее, и так далее. И потому с точки зрения быстродействия файловых операций выгоден максимальный размер логического блока файловой системы.
С другой стороны, увеличение размера логического блока ведет к непроизводительному расходу дискового пространства: данные файлов, размер которых меньше блока файловой системы, все равно занимают его целиком. Также целый блок потребуется и для хранения файловых хвостов, то есть остатков сверх объема, кратного размеру логического блока. Для борьбы с этим в UFS введено понятие фрагмента - логической части блока файловой системы, которая может быть записана или считана независимо от остальной его части.
Ясно, что размер фрагмента все равно не может быть меньше физического блока. При этом UFS накладывает на него и встречное ограничение - минимальный размер фрагмента определяется в 1/8 логического блока. Другие же возможные значения - 1/4 и 1/2 блока файловой системы (очевидно, что выделение фрагмента в размере блока равносильно отказу от фрагментации блока вообще, хотя это, как будто бы, не запрещено).
Каким образом определяется принадлежность блока данных тому или иному файлу? К помощью индексной таблицы, именуемой также таблицей индексных дескрипторов или таблицей inodes. Она образована некоторым (конечным) количеством записей фиксированной длины (128 байт в UFS, 256 - в UFS2), каждая из которых однозначно соответствует одному файлу, как реально существующему, так и только могущему быть созданным.
Такая запись индексной таблицы носит название inode. Каждый из них содержит так называемые метаданные файла. Для реально существующего файла они включают в себя идентификатор, тип файла и еще кое-какие атрибуты, рассмотренные ранее. Для файлов несуществующих свободные записи в таблице inodes просто резервируются.
А поскольку количество этих записей - величина конечная, именно объем индексной таблицы и определяет, сколько файлов реально может быть создано в данной файловой системе. Исчерпание свободных записей в ней приводит к тому, что, вне зависимости от объема свободного дискового пространства, ни одного нового файла создать не удастся.
Таблицы inodes содержатся в блоке группы цилиндров, наряду с картами свободных/занятых inodes и карты свободных/занятых блоков данных). Это имеет целью размещение inodes и относящихся к ним блоков данных максимально близко друг к другу, что, кроме повышения производительности (за счет минимизации перемещений головок), влечет за собой и сведение к минимуму той самой внешней фрагментации данных, о которой, как факторе несущественном, упоминалось выше.
Однако сведений об объеме индексной таблицы в блоке группы цилиндров не обнаруживается. Ибо место их размещения - тот самый суперблок, который идет первым в любом разделе (вообще-то первым будет загрузочный блок, но реально он занят только в загрузочном же разделе - во всех остальных для него просто зарезервировано место). А также дублируется в каждой группе цилиндров - чем обеспечивается устойчивость к механическим повреждениям диска. Кроме этого, в суперблоке же записывается размер блока файловой системы и их суммарное количество, число блоков данных, размер блокового фрагмента и их число в блоке, число блоков, реально занятых файлами, объем партиции, перманентно резервируемый как свободный, и еще множество характеристик файловой системы.
При всех своих многочисленных достоинствах, файловые системы BSD не могут похвастаться одним - быстродействием. Причина - в частично синхронном ее режиме, принятом по умолчанию. Конечно, UFS может быть смонтирована и чисто асинхронно, но в этом случае резко падает ее устойчивость к сбоям, при относительно небольшом выигрыше в производительности. И при этом, не будучи журналируемой, сама по себе не имеет механизма гарантии собственной целостности.
Исправлению обоих недостатков служит так называемый механизм SoftUpdates (оставим этот термин без перевода - варианты оного типа "мягких обновлений" не только не блещут литературным изяществом, но и сути дела не проясняют).
Он детально описан в специальной статье Макказика и Ганджера, смысла пересказывать которую я не вижу (благо она имеется в русском переводе - http://www.osp.ru/os/1999/07-08/13.htm). В двух же словах суть этого механизма - в сведении к минимуму синхронных операций записи без явно асинхронного манипулирования метаданными, с одной стороны, но и без предварительного журналирования метаданных (как в файловых системах типа ReiserFS или XFS) - с другой.
Реализуется это за счет так называемых зависимостей обновления. Что такое эти зависимости - интуитивно ясно из примера создания нового (для простоты - пустого) файла. Для этого требуется:
С точки зрения целостности файловой системы, эти операции должны быть выполнены именно в указанной последовательности. То есть наличие в каталоге имени файла с идентификатором незаполненного (еще не созданного или уже уничтоженного) inode - явный непорядок: именно для исправления такого рода безобразий и запускается программа проверки диска после аварийного завершения сеанса.
Выполнять связанные зависимостями операции обновления в синхронном режиме - долго (каждая потребует своего обращения к диску), в чисто асинхронном - нет гарантии сохранения последовательности (обновления в кэше могут быть записаны тогда, когда бог на душу положит). Так вот, механизм SoftUpdates обеспечивает, с одной стороны, контроль за последовательностью выполнения зависимых обновлений (что способствует целостности состояния файловой системы), с другой - группирует их в единую атомарную операцию синхронного обращения к диску, за счет сокращения числа коих и растет производительность.В этом и состоит объяснение парадокса SoftUpdates - неожиданного увеличения и надежности, и быстродействия.