Squashfs это файловая система с применением сжатия данных, используемая в Linux и монтируемая как read-only.
Она использует алгоритмы zlib, lz4, lzo или xz для компрессии файлов, inode и директорий. Inode в системе очень маленькие, и все блоки упакованы для минимизации лишних затрат на хранение данных. Поддерживаются размеры блока больше 4K, вплоть до максимума 1 мегабайт (размер блока по умолчанию 128K).
Squashfs предназначена для использования как обычная файловая система с доступом только для чтения (read-only filesystem), применяемая для архивов (например в случаях, где может использоваться файл .tar.gz), и в системах, где есть ограничения на размер блока или объем памяти (таких как embedded системы) и требуются низкие накладные расходы.
[1. Функциональные возможности Squashfs]
Сравнение Squashfs с Cramfs:
Параметр |
Squashfs |
Cramfs |
Max filesystem size |
264 |
256 MiB |
Max file size |
~2 TiB |
16 MiB |
Max files |
unlimited |
unlimited |
Max directories |
unlimited |
unlimited |
Max entries per directory |
unlimited |
unlimited |
Max block size |
1 MiB |
4 KiB |
Metadata compression |
yes |
no |
Directory indexes |
yes |
no |
Sparse file support |
yes |
no |
Tail-end packing (fragments) |
yes |
no |
Exportable (NFS etc.) |
yes |
no |
Hard link support |
yes |
no |
“.” and “..” in readdir |
yes |
no |
Real inode numbers |
yes |
no |
32-bit uids/gids |
yes |
no |
File creation time |
yes |
no |
Xattr support |
yes |
no |
ACL support |
no |
no |
Squashfs сжимает данные, inode-ы и директории. Дополнительно данные inode и директорий очень сильно ужимаются, и упаковываются с экономией каждого байта. Каждый compressed inode в среднем занимает 8 байт (точная длина зависит от типа файла, т. е. для обычного файла, директории, символической ссылки, block/char устройства у inode будут разные размеры).
[2. Использование Squashfs]
Поскольку squashfs это read-only файловая система, то для её формирования необходимо использовать программу mksquashfs. Эту программу и другие утилиты можно получить на сайте http://www.squashfs.org. Инструкции по использованию также можно найти на этом сайте.
Дерево разработки squashfs-tools теперь находится на kernel.org [3].
2.1. Опции монтирования
errors=%s |
Указывает, должны ли ошибки squashfs вызывать kernel panic, или нет.
continue |
Ошибки не приводят к панике ядра (по умолчанию). |
panic |
Вызывается паника, когда происходят ошибки, наподобие как это работает в других файловых системах (например btrfs, ext4, f2fs, GFS2, jfs, ntfs, ubifs).
Это позволяет ядру выполнить дамп памяти, и отладочной информации, что полезно для анализа и отладки причины проблемы. |
|
threads=%s |
Выбирает режим декомпрессии или количество потоков.
Если установлено SQUASHFS_CHOICE_DECOMP_BY_MOUNT:
single |
Используется однопоточная декомпрессия (по умолчанию).
Только один блок (данных или метаданных) может быть декомпрессирован в любой момент времени. Это ограничивает использование CPU и памяти по минимуму, но также приводит к плохой производительности на параллельных потоках ввода/вывода, когда применяются многоядерные CPU. |
multi |
Используется до двух параллельных декомпрессора на ядро.
Если у вас применяется параллельная рабочая нагрузка по вводу/выводу, и у вашей системы есть достаточное количество памяти, использование этой опции может улучшить общую производительность ввода/вывода. Этот подход динамически выделяет декомпрессоры на основании запросов. |
percpu |
Используется максимум один декомпрессор на ядро.
Этот вариант использует переменные percpu, чтобы гарантировать, что декомпрессия сбалансирована по нагрузке между ядрами. |
1|2|3|... |
Конфигурируется количество потоков, используемых для декомпрессии.
Верхний предел num_online_cpus() * 2. |
Если не установлено SQUASHFS_CHOICE_DECOMP_BY_MOUNT, и установлены оба SQUASHFS_DECOMP_MULTI и SQUASHFS_MOUNT_DECOMP_THREADS:
2|3|... |
Конфигурируется количество потоков, используемых для декомпрессии.
Верхний предел num_online_cpus() * 2. |
|
[3. Дизайн файловой системы Squashfs]
Файловая система squashfs состоит из максимум 9 частей, упакованных друг с другом с выравниванием в 1 байт:
---------------- | superblock | |----------------| | опции | | сжатия | |----------------| | блоки данных | | и фрагменты | |----------------| | таблица inode | |----------------| | таблица | | директорий | |----------------| | таблица | | фрагментов | |----------------| | таблица | | экспорта | |----------------| | lookup-таблица | | uid/gid | |----------------| | таблица | | xattr | ----------------
Сжатые блоки данных записываются в файловую систему, когда файлы считываются из исходной директории и проверяются на дубликаты. Как только все данные файлов записаны, записываются завершенные таблица inode, directory, fragment, export, uid/gid lookup и xattr.
3.1. Опции компрессии. Упаковщики могут опционально поддерживать опции специфические для компрессии (например размер словаря, dictionary size). Если используются опции компрессии не по умолчанию, то они здесь сохраняются.
3.2. Inode. Метаданные (inode-ы и директории) упакованы в 8 килобайтные блоки. Каждый упакованный блок снабжен префиксом из 2 байт, верхний бит установлен, если блок не сжат. Блок будет не сжат, если установлена опция -noI, или если сжатый блок получился больше, чем несжатый блок.
Inode-ы упаковываются а блоки метаданных, и они не выравнены на границы блока, так что inode-перекрывают сжатые блоки. Inode-ы идентифицируются 48-разрядным числом, кодирующим место размещения сжатого блока метаданных, содержащего inode, и байтовое смещение в блоке, где размещен inode(< block, offset>).
Чтобы добиться максимальной компрессии, существуют разные inode для каждого типа файла (обычный файл, директория, устройство и т. п.), так что содержимое inode и его длина меняются в зависимости от тира.
Чтобы дополнительно улучшить компрессию, определены 2 типа inode обычного файла и inode директории: inode-ы, оптимизированы под часто встречающиеся обычные файлы и директории, и расширенные типы, где должна храниться дополнительная информация.
3.3. Directories. Наподобие inode-ов, директории упакованы в сжатые блоки метаданных, сохраненные в таблицу директорий. К директориям осуществляется доступ с помощью начального адреса метаблока, содержащего директорию, и смещения в декомпрессированном блоке (< block, offset>).
Директории организованы несколько более сложным способом, и они не просто перечисляют имена файлов. Организация использует тот факт, что (в большинстве случаев) inode-ы файлов будут в том же сжатом блоке метаданных, и поэтому могут совместно использовать начало блока. Таким образом, директории организованы в виде двухуровневого списка: заголовка директории (directory header), где содержится общее значение начала блока, и последовательность элементов директории, каждый из которых использует общее начало блока. Новый заголовок директории записывается один раз, если меняется начало блока inode. Заголовок директории / элемент списка директории повторяются столько раз, сколько это необходимо.
Директории отсортированы, и могут содержать индекс директорий, что ускоряет поиск файла. Индексы директорий хранят одну запись на каждый метаблок, при этом каждая запись хранит сопоставление индекса/имени файла с первым заголовком директории в каждом блоке метаданных. Директории отсортированы в алфавитном порядке, и при поиске индекс сканируется линейно, ища первое имя файла в алфавитном порядке больше, чем искомое имя файла. На этом этапе было найдено местоположение блока метаданных, в котором находится имя файла. Общая идея индекса состоит в том, чтобы гарантировать, что только один блок метаданных должен быть распакован для выполнения поиска, независимо от длины каталога. Эта схема имеет то преимущество, что она не требует дополнительных затрат памяти и не требует много дополнительного пространства на диске.
3.4. Данные файла. Обычные файлы состоят из последовательности смежных сжатых блоков, и/или фрагментов сжатых блоков (хвостов упакованного блока). Сжатый размер каждого блока сохраняется в списке блоков, содержащемся в inode файла.
Чтобы ускорить доступ к блокам данных при чтении "больших" файлов (256 Мбайт или больше), код реализует кэш-память индекса, которая кэширует сопоставление из индекса блока в расположение блока данных на диске.
Кэш индекса позволяет Squashfs обрабатывать большие файлы (до 1.75 TiB), сохраняя при этом на диске простой и эффективный по размеру список блоков. Кэш разбивается на слоты, кэшируя до восьми 224 GiB файлов (128 KiB-блоков). Большие файлы используют несколько слотов, файлы 1.75 TiB используют все 8 слотов. Кэш-память индекса спроектирована таким образом, чтобы обеспечивать эффективное использование памяти, и по умолчанию использует 16 KiB.
3.5. Таблица поиска фрагментов. Обычные файлы могут содержать индекс фрагмента, который с помощью таблицы поиска фрагментов (fragment lookup table) сопоставляется с расположением фрагмента на диске и сжатым размером. Таблица поиска фрагментов сама по себе сохраняется сжатой в блоках метаданных. Для их поиска используется вторая индексная таблица. Эта вторая индексная таблица для ускорения доступа (и по той причине, что она мала) считывается во время монтирования и кэшируется в памяти.
3.6. Таблица поиска uid/gid. Для эффективного использования хранилища обычные файлы хранят индексы uid и gid, которые преобразовываются в 32-битные uid/gid с помощью таблицы поиска идентификаторов (id look up table). Эта таблица сохраняется сжатой в блоках метаданных. Для их поиска используется вторая индексная таблица. Эта вторая индексная таблица для ускорения доступа (и по той причине, что она мала) считывается во время монтирования и кэшируется в памяти.
3.7. Таблица экспорта. Чтобы файловые системы Squashfs были экспортируемыми (через NFS и т. п.), файловые системы могут опционально (что запрещается опцией -no-exports утилиты Mksquashfs) содержать номер inode для таблицы поиска местоположения на диске inode (inode disk location lookup table). Это необходимо, чтобы позволить Squashfs отобразить номера inode, переданные в дескрипторах файлов (filehandle) на расположения inode на диске, что необходимо, когда код экспорта восстанавливает expired/flushed inode-ы.
Эта таблица сохраняется сжатой в блоках метаданных. Для их поиска используется вторая индексная таблица. Эта вторая индексная таблица для ускорения доступа (и по той причине, что она мала) считывается во время монтирования и кэшируется в памяти.
3.8. Таблица xattr. Здесь содержатся расширенные атрибуты для каждого inode. Атрибуты xattr для каждого inode сохранены в списке, каждый элемент списка содержит поля типа (type), имени (name) и значения (value). Поле type кодирует префикс xattr ("user.", "trusted." и т. п.), и также кодирует, как должны интерпретироваться поля name/value. В настоящее время тип указывает, хранится ли значение в строке (в этом случае поле значения содержит значение xattr) или хранится вне строки (в этом случае поле значения хранит ссылку на то, где хранится фактическое значение). Это позволяет хранить большие значения вне строки, улучшая производительность сканирования и поиска. Также это позволяет устранить дублирование значений, когда значение сохраняется один раз, и все другие появления того же значения хранятся в виде ссылки на это значение.
Список xattr упакован в сжатых блоках метаданных по 8K. Чтобы уменьшить накладные расходы в inodes, вместо сохранения расположения списка xattr на диске внутри каждого inode, сохраняется 32-битный xattr id. Этот xattr id сопоставляется с расположением списка xattr с помощью второй таблицы поиска xattr id.
[4. TODO и нерешенные вопросы]
4.1. Список TODO. Реализация поддержки ACL (списков управления доступом).
4.2. Внутренняя кэш Squashfs. Блоки в Squashfs сжатые. Чтобы избежать многократного распаковки недавно полученных данных, Squashfs использует два небольших кэша метаданных и фрагментов.
Кэш не используется для файловых блоков данных, они распаковываются и кэшируются в page-cache обычным способом. Кэш используется для временного кэширования фрагментов и блоков метаданных, которые были прочитаны в результате доступа к метаданным (то есть, к inode или директории) или фрагментам. Поскольку метаданные и фрагменты упакованы вместе в блоки (чтобы получить большее сжатие), считывание конкретной части метаданных или фрагмента будет извлекать другие метаданные/фрагменты, которые были упакованы с ним, они из-за локальности ссылки могут быть считаны в ближайшем будущем. Временное кэширование гарантирует, что они будут доступны для доступа в ближайшем будущем, не требуя дополнительного чтения и распаковки.
В будущем этот внутренний кэш может быть заменен реализацией, которая использует кэш страниц ядра (kernel page cache). Поскольку кэш страниц работает с единицами размера страниц, это может создать дополнительную сложность с точки зрения блокировки и связанных условий гонки.
[Ссылки]
1. Squashfs 4.0 Filesystem https://docs.kernel.org/filesystems/squashfs.html. 2. Mailing list: squashfs-devel@lists.sourceforge.net, Web site: www.squashfs.org. 3. git://git.kernel.org/pub/scm/fs/squashfs/squashfs-tools.git. |