Что такое superblock в linux

Обновлено: 08.07.2024

У меня есть компьютер с двойной загрузкой Ubuntu / Windows более 5 лет. Последняя рабочая конфигурация была: Windows 10 и Ubuntu 16.04 LTS.

Я устанавливаю Ubuntu 16.10 на раздел Linux и у меня возникают некоторые проблемы:

  • Если я загружаюсь только на Linux, проблем нет, все работает нормально.
  • Если я загружаю Windows, нет проблем, Windows работает нормально.
  • Если я загружаю Linux после Windows, у меня появляется ошибка суперблока и я перехожу на busybox.

Если я восстановлю суперблок с

Я могу загрузить Linux, но после другой загрузки Windows повторяется та же проблема.

Конечно, я попробовал найденные решения:

  • chkdsk и testdisk пока на окнах.
  • Я удаляю расширенный раздел и позволяю установщику Linux воссоздать их.
  • Создайте с помощью and, расширенный раздел + ext4, поменяйте местами и выберите эти разделы в процессе установки.

Таблица разделов ( fdisk -lu ):

Я сделал еще несколько тестов без успеха:

  • Я удалил расширенный раздел Linux
  • Я расширил раздел Windows на полный диск
  • Я запустил chkdsk /F /R на этом новом разделе Windows → через пару часов все хорошо
  • Я переустановил Ubuntu 16.10 и позволил программе установки изменить размер диска и создать раздел ext4 и swap.
  • Я загружаю Linux → нет проблем
  • Я загружаю Windows → нет проблем (я попадаю в меню Windows, которое позволяет мне выбирать между Windows и Linux, я выбираю Windows)
  • Я загружаю Linux: та же проблема снова (ошибка суперблока)

Обновление: точная ошибка суперблока:

Чтобы восстановить это:

работает нормально, но после загрузки Windows проблема снова возникает .

Обновление 2: из W10 я могу смонтировать раздел linux ext4 с помощью ext2 инструмента W10 . Работает нормально, я вижу файлы. Но после этого та же проблема, когда я загружаюсь на Linux: ошибки суперблока.

Конечно, если я просто загружаю W10 (без логина) и перезагружаюсь linux, проблема тоже есть.

Проблема была с W10 1511, я обновился до W10 1607: та же проблема

Я видел, что не был один на земле с этой проблемой, но я не видел никакого решения.

update2: я удалил раздел linux и swap и создал новый раздел из windows. Затем я переустановил Ubuntu 16.10 => та же проблема снова

обновление 3: начало решения Я, возможно, нашел решение. При входе в Windows: С помощью команды diskpart , когда установлен том Linux, offline больше не возникает проблем при загрузке Linux.

После форматирования диска или раздела сектора на диске разделены на небольшие группы. Такая группа секторов называется блоком. Размер блока может быть разным и задается как параметр ключа команды форматирования. Например

ключ -b задает размер блока в байтах, в данном случае размер блока будет 4096 байт

Размер блока может быть разным. Это зависит от типа файловой системы

При выборе размера блока нужно учесть ряд моментов

  • Максимальный размер файла
  • Максимальный размер файловой системы
  • Производительность

Размер блока влияет на скорость чтения/записи с диска. Представим себе файл размеров в несколько сот мегабайт, который считывается с диска блоками по 1Кб. Тот же файл будет считываться быстрее если размер блока файловой системы будет 4Кб или 8Кб. Это ясно. Поэтому при форматировании имеет смысл задать блок большего размера, если планируется использовать файлы большого размера

Также верно и обратное утверждение. В случае хранения небольших файлов лучше использовать блоки минимального размера

Ядро Linux работает с размером блока файловой системы, а не с размером сектора диска (обычно 512 байт). Важно понимать, что размер блока файловой системы не может быть меньше размера сектора диска и всегда будет кратным ему. Также ядро ожидает, что размер блока файловой системы будет меньше или равно размеру системной страницы

Размер системной страницы можно увидеть выполнив команду

Группы блоков файловой системы

Блоки, о которых мы говорили ранее обьеденяются в группы блоков, что позитивно отражается на операциях чтения/записи так как уменьшается время чтения/записи больших обьемов данных

Файловая система EXT разбивает все доспупное пространство на группы блоков равного размера. Эти группы располагаются последовательно, одна за другой

Загрузочный блок Группа блоков 1 Группа блоков 2 Группа блоков 2 Группа блоков 3

Количество блоков в группе неизменно и может быть расчитано по формуле

Взглянем на вывод команды mke2fs

Отметим то, о чем говорили выше

  • Размер блока 4096 байт
  • 800 блоковых групп
  • 32768 блоков в группе (8*4096)

Также видны блоки в которых хранятся резервные копии суперблока

Так что же такое суперблок?

Самым простым определением суперблока могло бы быть следующее утверждение

Аналогично тому, как i-ноды хранят метаданные о файлах, суперблок хранит метаданные о файловой ситеме. Если вдруг суперблок поврежден, то не возможно будет примонтировать файловую систему. Обычно при загрузке система проверяет суперблок и при необходимости исправляет его, что в результате приводит к корректному монтированию файловых систем

Некоторые данные, которые хранятся в суперблоке. Например

  • Количество блоков в файловой системе
  • Количество свободных блоков в файловой системе
  • Количество i-нод в блоковой группе
  • Блоки в блоковой группе
  • Количество запусков файловой системы со времени последней проверки fsck
  • UUID файловой системы
  • Состояние файловой системы (была ли корректно размонтирована, обнаруженые ошибки и т.д.)
  • Тип файловой системы
  • Операционная система в которой была отформатирована данная файловая система
  • Время последнего монтирования
  • Время последней записи

Основная копия суперблока хранится в самой первой группе блоков. Она названа основной, потому что считывается системой в процессе монтирования файловой системы. Так как отсчет блоковых групп начинается с 0 то можно говорить о том, что суперблок хранится в начале блоковой группы 0

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

Как увидеть, что хранится в суперблоке?

Для этого воспользуемся командой dumpe2fs

Еще один вывод команды показывает информацию о суперблоке

Как восстановить поврежденный суперблок?

Для начала нужно проверить файловую систему утилитой fsck

В случае если fsck обнаружила ошибку чтения суперблока можно попробовать сделать следующее:

Для начала определим где расположены резервные копии суперблока. Для этого выполняем

ключ -n говорит команде не создавать файловую систему, но показать вывод какой мог бы быть при реальном создании файловой системы

Далее восстановливаем суперблок из бекапа при помощи e2fsck

В данном случае в блоке 819200 хранится резервная копия суперблока. После применения команды пробуем снова монтировать файловую систему. Либо как вариант использовать ключ sb команды mount, который указывает на расположение копии суперблока

В данном случае считываем копию суперблока из блока 819200

Системный администратор. В сисадминстве с 2000 года. Участник cyberforum

А ты знаешь особенности файловой системы Linux? Linux обаладет очень мощной файловой системы, возможности которой знают далеко не все пользователи. Сегодня ты станешь одним из них, а чтобы статья не была сугубо теоретической и сухой мы рассмотрим ряд практических примеров на каждый день. Один из них - восстановление загрузчика GRUB после очередной переустановки Windows.

Архитектура файловой системы, ее основные структуры

Прежде, чем приступить к рассмотрению архитектуры файловой системы Linux, нужно разобраться, что вообще такое файловая система (ФС). Можно встретить различные определения, например, ФС - это способ представления информации на носителе данных. Или ФС - часть операционной системы, обеспечивающая выполнение операций над файлами. Но более точным будет объединение этих двух определений. Ведь, грубо говоря, любая файловая система состоит как минимум из двух уровней - уровня представления данных и набора системных вызовов для работы с данными. Зачастую операционная система может работать с различными файловыми системами, например, с основной файловой системой (используется на жестком диске) и с файловой системой CD - ISO9660. Поэтому ОС должна обеспечить приложениям стандартный интерфейс, позволяющий обращаться к файлам на разных ФС прозрачно, то есть программист не должен вникать в тонкости каждой файловой системы. Например, в никсах для открытия файла используется системный вызов open(). Программа просто вызывает open(), передав ему имя файла, а на какой ФС расположен этот файл - дело третье. Теперь посмотри на рис. 1, на нем изображено все то, что было сказано выше. Зеленым отмечен пользовательский уровень, а желтым - уровень ядра.

Рис.1 Архитектура файловой системы

Приложение может использовать функции glibc (библиотека GNU C) или же напрямую использовать системные вызовы ядра - тут уж как будет угодно программисту. Использовать функции glibc удобнее, но, вызывая непосредственно системные вызовы, (например, open(), read(), write(), close()) можно немного повысить производительность приложения - ведь ты минуешь glibc, которая все равно использует те же системные вызовы.

VFS - это виртуальная файловая система. Именно она позволяет добиться существующего сейчас уровня абстракции. Каждая файловая система имеет свои особенности. Если бы не было VFS, то пришлось бы разрабатывать разные версии системных вызовов для каждого типа поддерживаемой файловой системы, например, open_ext3() - для открытия файла, находящегося на файловой системе ext3, или open_vfat() - для ФС VFAT. Другими словами, VFS делает системные вызовы независимыми от типа используемой файловой системы.

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

Копаем глубже

Рисунок 1 - это общее представление о файловой системе Linux. Настало время копнуть глубже. Рассмотрим логическую структуру файловой системы ext3. Физически жесткий диск разбит на секторы размером 512 байт. Первый сектор дискового раздела в любой файловой системе считается загрузочной областью. В первичном разделе эта область содержит загрузочную запись — фрагмент кода, который инициирует процесс загрузки операционной системы при запуске. На других разделах эта область не используется. Остальные секторы объединены в логические блоки размером 1, 2 или 4 Кб. Логический блок есть наименьшая адресуемая порция данных: данные каждого файла занимают целое число блоков. Блоки, в свою очередь, объединяются в группы блоков. Группы блоков и блоки внутри группы нумеруются последовательно, начиная с 1.

Раздел диска, на котором сформирована файловая система ext3, может быть представлен такой схемой (рис. 2).

Рис.2 Структура файловой системы

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

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

После суперблока следует описание (дескриптор) группы блоков. Хранящаяся в нем информация позволяет найти битовые карты блоков и индексных дескрипторов, а также таблицу индексных дескрипторов.

Структуру системного блока можно найти в /usr/src/linux/include/linux/fs.h:

Битовой картой блоков (block bitmap) называется структура, каждый бит которой показывает, отведен ли такой же по счету блок какому-либо файлу. Значение 1 показывает, что блок занят. Эта карта служит для поиска свободных блоков в тех случаях, когда надо выделить место под файл. Битовая карта индексных дескрипторов выполняет аналогичную функцию по отношению к таблице индексных дескрипторов: показывает, какие именно дескрипторы заняты. Каждому файлу соответствует один и только один индексный дескриптор (inode), который идентифицируется своим порядковым номером — индексом файла. В индексном дескрипторе хранятся метаданные файла. Среди них — все атрибуты файла, кроме его имени, и указатель на данные файла.

Для обычного файла или каталога этот указатель представляет собой массив из 15 адресов блоков. Первые 12 адресов в этом массиве являются прямыми ссылками на номера блоков, в которых хранятся данные файла. Если данные не помещаются в 12 блоков, то включается механизм косвенной адресации. Следующий адрес в этом массиве является косвенной ссылкой, то есть адресом блока, в котором хранится список адресов следующих блоков с данными из этого файла. Сколько блоков с данными можно так адресовать? Адрес блока занимает 4 байта, блок, как уже сказано, имеет размеры 1, 2 или 4 Кб. Значит, путем косвенной адресации можно разместить 256 — 1024 блока.

А если файл еще длиннее? Следующий адрес в массиве-указателе указывает на блок двойной косвенной адресации (double indirect block). Этот блок содержит список адресов блоков, которые, в свою очередь, содержат списки адресов следующих блоков данных.

И, наконец, последний адрес в массиве-указателе задает адрес блока тройной косвенной адресации, то есть блока со списком адресов блоков, которые являются блоками двойной косвенной адресации.

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

Во-первых, одному индексному дескриптору может соответствовать любое количество имен, приписанных к разным каталогам, и все они являются настоящими. Количество имен (жестких ссылок) учитывается в индексном дескрипторе. Именно это количество можно увидеть по команде «ls –l». Во-вторых, удаление файла означает просто удаление записи о нем из данных каталога и уменьшение на 1 счетчика ссылок. В-третьих, сопоставить имя можно только номеру индексного дескриптора внутри одной и той же файловой системы, именно поэтому нельзя создать жесткую ссылку в другую файловую систему (символическую — можно, у нее другой механизм хранения).

Сам каталог таким же образом приписан к своему родительскому каталогу. Корневой каталог всегда записан в индексный дескриптор с номером 2 (номер 1 отведен для списка адресов дефектных блоков). В каждом каталоге хранится ссылка на него самого и на его родительский каталог — это и есть псевдоподкаталоги «.» и «..».

Таким образом, количество ссылок на каталог равно количеству его подкаталогов плюс два. Данные каталога представляют собой связный список с записями переменной длины и выглядят примерно так, как показано на рис 3.

Рис.3 Структура каталога в ext3

А как же файлы физических устройств? Они могут находиться в тех же каталогах, что и обычные файлы: в каталоге нет никаких данных, говорящих о принадлежности имени файлу на диске или устройству. Разница находится на уровне индексного дескриптора. Если иноде обычного файла указывает на дисковые блоки, где хранятся его данные, то в иноде файла устройства содержится указатель на список драйверов устройств в ядре — тот элемент списка, который соответствует старшему номеру устройства (рис. 4).

Рис.4 Разница между обычным файлом и файлом устройства

Монтирование

Чтобы получить доступ к файлам и каталогам, находящимся за пределами корневой ФС (на других разделах жесткого диска или на других носителях), нужно примонтировать их к корневой файловой системе.

Для монтирования файловой системы предназначена программа mount, для размонтирования - umount. Общий формат вызова (наиболее часто используемый) следующий:

После монтирования файловая система добавляется в список смонтированных файловых систем в ядре:

Последняя структура mnt_list - это и есть список смонтированных файловых систем. Пользователь может просмотреть этот список в файле /etc/mtab.

Некоторые особые операции с файловой системой

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

Команда dd читает данные с устройства /dev/zero и записывает их в файл fs.img. В качестве данных будет просто поток нулей, причем не чисел ноль (ANSI-код 48), а неотображаемых символов NULL (ANSI-код 0). Данные читаются и записываются блоками по 1 Кб (bs=1k), и общее количество блоков равно 30000. Таким образом, на выходе получаем файл размером почти 30 Мб, заполненный символами NULL. После этого командой losetup мы превратим наш файл в блочное устройство:

Теперь мы можем обращаться к устройству /dev/loop0, как к обычному блочному устройству (блочным называется устройство, обмен данными с которым производится блоками, например, секторами диска). Создадим файловую систему на устройстве /dev/loop0 с помощью команды mke2fs:

Все готово для монтирования созданной файловой системы. Мы подмонтируем ее к каталогу /mnt/fs, который нужно создать заранее:

После этого мы можем работать с каталогом /mnt/fs, как с обычным каталогом файловой системы. Никаких ограничений нет. Внутри этого каталога мы можем создать еще один пустой файл и тоже превратить его в файловую систему, а затем подмонтировать. Для упрощения процесса вместо команды losetup можно указать параметр '-o loop' команды mount, например:

Для размонтирования файла с файловой системой, как обычно, используем umount:

Далее следует удалить устройство /dev/loop0 (если ты его создавал):

Немного практики. Alcohol и UltraISO средствами Linux!

Все это хорошо и очень занимательно, но разберемся, как можно применить полученные знания на практике. Пустой файл, созданный с помощью dd, нам не нужен. Что мы будем в нем хранить? Записывать файлы? А не проще ли записать их в обычные каталоги и не морочить себе голову? Но dd можно использовать не только для создания пустых файлов, но и для создания образов CD/DVD:

Данная команда создаст образ CD-диска и запишет его в домашний каталог пользователя под именем image.iso. Затем можно обратиться к файлам созданного образа без его записи на болванку. Для этого нужно подмонтировать его к корневой файловой системе (каталог /mnt/fs должен существовать):

После этого ты можешь работать с /mnt/fs, как с обычным каталогом. Но работа с ISO-образами - это не единственное применение loop-устройств. Может так получиться, что после установки системы окажется, что оперативной памяти не хватает. Покупать дополнительный модуль памяти не хочется, а переразбивать жесткий диск с целью увеличения размера раздела подкачки - тем более. Тогда можно создать пустой файл, отформатировать его как файл подкачки и подключить к системе:

Первая команда создает пустой файл размером 256 Мб. Вторая - форматирует его как своп. Как видишь, для Linux нет особой разницы, с чем работать - с файлом или с блочным устройством. Ты запросто можешь отформатировать обычный файл в любую файловую систему. Третья команда подключает созданный своп-файл к системе. Можешь ввести команду free, чтобы убедиться, что файл подкачки подключен. Только не забудь добавить последнюю команду в сценарии запуска системы, чтобы не вводить ее после каждой перезагрузки.

Изменение корневой файловой системы

Linux позволяет изменять корневую файловую систему. Предположим, требуется восстановить загрузчик Linux. Ты загрузился с LiveCD, но тебе нужно подключить корневую файловую систему так, чтобы все изменения, производимые вводимыми командами, относились именно к корневой файловой системе на жестком диске, а не к корневой файловой системе LiveCD. Другими словами нам нужно изменить корневую файловую систему. Делается это просто - с помощью команды chroot:

Монтирование каталога к каталогу

В Linux можно подмонтировать каталог к каталогу, а не только каталог к устройству. Делается это с помощью все той же команды mount, но запущенной с параметром '--bind':

Восстановление загрузчика GRUB: пошаговое руководство

Ты переустановил Windows, а она установила в MBR свой загрузчик и теперь нельзя загрузить Linux? Не переустанавливать же еще и Linux из-за такой мелочи! Для восстановления загрузчика GRUB нужно загрузиться с LiveCD (подойдет любой LiveCD с любым дистрибутивом Linux) и ввести следующие команды:

Все данные команды нужно вводить от имени root. Чтобы получить полномочия root, нужно использовать команды su или sudo. Например, в Ubuntu LiveCD нужно вводить все команды с использованием команды sudo, например,

Разберемся, что означают данные команды. Первая команда создает каталог /old, который будет использоваться в качестве точки монтирования. Вторая команда создает в этом каталоге подкаталог dev, который пригодится для монтирования devfs – псевдофайловой системы. Третья команда используется для монтирования корневой файловой системы дистрибутива Linux, установленного на жестком диске к каталогу /old. Предположим, что твой дистрибутив Linux был установлен в раздел /dev/sda5. Поэтому нужно ввести команду:

mount /dev/sda5 /old

После этого нужно подмонтировать каталог /dev к каталогу /old/dev. Это делается с помощью все той же команды mount, но с параметром --bind. Команда chroot заменяет корневую систему нашего LiveCD на корневую систему дистрибутива, установленного на винчестере. Тебе остается лишь ввести команду:

Данная команда установит загрузчик GRUB так, как он был установлен до этого. После установки GRUB нужно перезагрузить компьютер (команда reboot).

Прим перев.: Автор оригинальной статьи — испанский Open Source-энтузиаст nachoparker, развивающий проект NextCloudPlus (ранее известен как NextCloudPi), — делится своими знаниями об устройстве дисковой подсистемы в Linux, делая важные уточнения в ответах на простые, казалось бы, вопросы…

Сколько пространства занимает этот файл на жёстком диске? Сколько свободного места у меня есть? Сколько ещё файлов я смогу вместить в оставшееся пространство?


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

Однако в современных Linux-системах такая интуиция может вводить в заблуждение. Давайте разберёмся, почему.

Размер файла

Что такое размер файла? Ответ вроде бы прост: совокупность всех байтов его содержимого, от начала до конца файла.

Зачастую всё содержимое файла представляется как расположенное байт за байтом:


Так же мы воспринимаем и понятие размер файла. Чтобы его узнать, выполняем ls -l file.c или команду stat (т.е. stat file.c ), которая делает системный вызов stat() .

В ядре Linux структурой памяти, представляющей файл, является inode. И метаданные, к которым мы обращаемся с помощью команды stat , находятся именно в inode.


Здесь можно увидеть знакомые атрибуты, такие как время доступа и модификации, а также i_size — это и есть размер файла, как он был определён выше.

Размышлять в терминах размера файла интуитивно понятно, но больше нас интересует, как в действительности используется пространство.

Блоки и размер блока

Для внутреннего хранения файла файловая система разбивает хранилище на блоки. Традиционным размером блока были 512 байт, но более актуальное значение — 4 килобайта. Вообще же при выборе этого значения руководствуются поддерживаемым размером страницы на типовом оборудовании MMU (memory management unit, «устройство управления памятью» — прим. перев.).

Файловая система вставляет порезанный на части (chunks) файл в эти блоки и следит за ними в метаданных. В идеале всё выглядит так:


… но в действительности файлы постоянно создаются, изменяются в размере, удаляются, поэтому реальная картина такова:


Это называется внешней фрагментацией (external fragmentation) и обычно приводит к падению производительности. Причина — вращающейся головке жёсткого диска приходится переходить с места на место, чтобы собрать все фрагменты, а это медленная операция. Решением данной проблемы занимаются классические инструменты дефрагментации.

Что происходит с файлами меньше 4 КБ? Что происходит с содержимым последнего блока после того, как файл был порезан на части? Естественным образом будет возникать неиспользуемое пространство — это называется внутренней фрагментацией (internal fragmentation). Очевидно, этот побочный эффект нежелателен и может привести к тому, что многое свободное пространство не будет использоваться, особенно если у нас большое количество очень маленьких файлов.

Итак, реальное использование диска файлом можно увидеть с помощью stat , ls -ls file.c или du file.c . Например, содержимое 1-байтового файла всё равно занимает 4 КБ дискового пространства:


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

Специфичные для файловой системы возможности

Помимо актуального содержимого файла ядру также необходимо хранить все виды метаданных. Метаданные inode'а мы уже видели, но есть и другие данные, с которыми знаком каждый пользователь UNIX: права доступа, владелец, uid, gid, флаги, ACL.


Наконец, существуют ещё и другие структуры — вроде суперблока (superblock) с представлением самой файловой системы, vfsmount с представлением точки монтирования, а также информация об избыточности, именные пространства и т.п. Как мы увидим далее, некоторые из этих метаданных также могут занимать значительное место.

Метаданные размещения блоков

Эти данные сильно зависят от используемой файловой системы — в каждой из них по-своему реализовано сопоставление блоков с файлами. Традиционный подход ext2 — таблица i_block с прямыми и непрямыми блоками (direct/indirect blocks).


Эту же таблицу можно увидеть в структуре памяти (фрагмент из fs/ext2/ext2.h ):


Для больших файлов такая схема приводит к большим накладным расходам, поскольку единственный (большой) файл требует сопоставления тысяч блоков. Кроме того, есть ограничение на размер файла: используя такой метод, 32-битная файловая система ext3 поддерживает файлы не более 8 ТБ. Разработчики ext3 спасали ситуацию поддержкой 48 бит и добавлением extents:


Идея по-настоящему проста: занимать соседние блоки на диске и просто объявлять, где extent начинается и каков его размер. Таким образом мы можем выделять файлу большие группы блоков, минимизируя количество метаданных и заодно используя более быстрый последовательный доступ.

Примечание для любопытных: у ext4 предусмотрена обратная совместимость, то есть в ней поддерживаются оба метода: непрямой (indirect) и extents. Увидеть, как распределено пространство, можно на примере операции записи. Запись не идёт напрямую в хранилище — из соображений производительности данные сначала попадают в файловый кэш. После этого в определённый момент кэш записывает информацию на постоянное хранилище.

Кэш файловой системы представлен структурой address_space , в которой вызывается операция writepages. Вся последовательность выглядит так:


… где ext4_map_blocks() вызовет функцию ext4_ext_map_blocks() или ext4_ind_map_blocks() в зависимости от того, используются ли extents. Если взглянуть на первую в extents.c , можно увидеть упоминания дыр (holes), о которых будет рассказано ниже.

Контрольные суммы

Файловые системы последнего поколения хранят также контрольные суммы (checksums) для блоков данных во избежание незаметного повреждения данных. Эта возможность позволяет обнаруживать и корректировать случайные ошибки и, конечно, ведёт к дополнительным накладным расходам в использовании диска пропорционально размеру файлов.

Более современные системы вроде BTRFS и ZFS поддерживают контрольные суммы для данных, а у более старых, таких как ext4, реализованы контрольные суммы для метаданных.

Журналирование

Возможности журналирования для ext2 появились в ext3. Журнал — циклический лог, записывающий обрабатываемые транзакции с целью улучшить устойчивость к сбоям питания. По умолчанию он применяется только к метаданным, однако можно его активировать и для данных с помощью опции data=journal , что повлияет на производительность.

Это специальный скрытый файл, обычно с номером inode 8 и размером 128 МБ, объяснение про который можно найти в официальной документации:

Журнал, представленный в файловой системе ext3, используется в ext4 для защиты ФС от повреждений в случае системных сбоев. Небольшой последовательный фрагмент диска (по умолчанию это 128 МБ) зарезервирован внутри ФС как место для сбрасывания «важных» операций записи на диск настолько быстро, насколько это возможно. Когда транзакция с важными данными полностью записана на диск и сброшена с кэша (disk write cache), запись о данных также записывается в журнал. Позже код журнала запишет транзакции в их конечные позиции на диске (операция может приводить к продолжительному поиску или большому числу операций чтения-удаления-стирания) перед тем, как запись об этих данных будет стёрта. В случае системного сбоя во время второй медленной операции записи журнал позволяет воспроизвести все операции вплоть до последней записи, гарантируя атомарность всего, что пишется на диск через журнал. Результатом является гарантия, что файловая система не застрянет на полпути обновления метаданных.

«Упаковка хвостов»

Возможность tail packing, ещё называемая блочным перераспределением (block suballocation), позволяет файловым системам использовать пустое пространство в конце последнего блока («хвосты») и распределять его среди различных файлов, эффективно упаковывая «хвосты» в единый блок.


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

Разрежённые файлы

Большинство современных файловых систем поддерживают разрежённые файлы (sparse files). У таких файлов могут быть дыры, которые в действительности не записаны на диск (не занимают дисковое пространство). На этот раз реальный размер файла будет больше, чем используемые блоки.


Такая особенность может оказаться очень полезной, например, для быстрой генерации больших файлов или для предоставления свободного пространства виртуальному жёсткому диску виртуальной машины по запросу.

Чтобы медленно создать 10-гигабайтный файл, который занимает около 10 ГБ дискового пространства, можно выполнить:


Чтобы создать такой же большой файл мгновенно, достаточно лишь записать последний байт… или даже сделать:


Или же воспользоваться командой truncate :


Дисковое пространство, выделенное файлу, можно изменить командой fallocate , которая делает системный вызов fallocate() . С этим вызовом доступны и более продвинутые операции — например:

  • Предварительно выделить пространство для файла вставкой нулей. Такая операция увеличивает и использование дискового пространства, и размер файла.
  • Освободить пространство. Операция создаст дыру в файле, делая его разрежённым и уменьшая использование пространства без влияния на размер файла.
  • Оптимизировать пространство, уменьшив размер файла и использование диска.
  • Увеличить пространство файла, вставив дыру в его конец. Размер файла увеличивается, а использование диска не меняется.
  • Обнулить дыры. Дыры станут не записанными на диск extents, которые будут читаться как нули, не влияя на дисковое пространство и его использование.


Команда cp поддерживает работу с разрежёнными файлами. С помощью простой эвристики она пытается определить, является ли исходный файл разрежённым: если это так, то результирующий файл тоже будет разрежённым. Скопировать же неразрежённый файл в разрежённый можно так:


… а обратное действие (сделать «плотную» копию разрежённого файла) выглядит так:


Таким образом, если вам нравится работать с разрежёнными файлами, можете добавить следующий алиас в окружение своего терминала (


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

(cache read miss) ext4_aops-> ext4_readpages() -> . -> zero_user_segment()

После этого сегмент памяти, к которому процесс пытается обратиться с помощью системного вызова read() , получит нули напрямую из быстрой памяти.

Файловые системы COW (copy-on-write)

Следующее (после семейства ext) поколение файловых систем принесло очень интересные возможности. Пожалуй, наибольшего внимания среди фич файловых систем вроде ZFS и BTRFS заслуживает их COW (copy-on-write, «копирование при записи»).

Когда мы выполняем операцию copy-on-write или клонирования, или копии reflink, или поверхностной (shallow) копии, на самом деле никакого дублирования extent'ов не происходит. Просто создаётся аннотация в метаданных для нового файла, которая отсылает к тем же самым extents оригинального файла, а сам extent помечается как разделяемый (shared). При этом в пользовательском пространстве создаётся иллюзия, что существуют два отдельных файла, которые можно отдельно модифицировать. Когда какой-то процесс захочет написать в разделяемый extent, ядро сначала создаст его копию и аннотацию, что этот extent принадлежит единственному файлу (по крайней мере, на данный момент). После этого у двух файлов появляется больше отличий, однако они все ещё могут разделять многие extents. Другими словами, extents в файловых системах с поддержкой COW можно делить между файлами, а ФС обеспечит создание новых extents только в случае необходимости.


Как видно, клонирование — очень быстрая операция, не требующая удваивания пространства, которое используется в случае обычной копии. Именно эта технология и стоит за возможностью создания мгновенных снапшотов в BTRFS и ZFS. Вы можете буквально клонировать (или сделать снапшот) всей корневой файловой системы меньше чем за секунду. Очень полезно, например, перед обновлением пакетов на случай, если что-то сломается.

BTRFS поддерживает два метода создания shallow-копий. Первый относится к подтомам (subvolumes) и использует команду btrfs subvolume snapshot . Второй — к отдельным файлам и использует cp --reflink . Такой алиас (опять же, для

/.bashrc ) может пригодиться, если вы хотите по умолчанию делать быстрые shallow-копии:

cp='cp --reflink=auto --sparse=always'

Следующий шаг — если есть не-shallow-копии или файл, или даже файлы, с дублирующимися extents, можно дедуплицировать их, чтобы они использовали (через reflink) общие extents и освободили пространство. Один из инструментов для этого — duperemove, однако учтите, что это естественным образом приводит к более высокой фрагментации файлов.

Если мы попытаемся теперь разобраться, как дисковое пространство используется файлами, всё будет не так просто. Утилиты вроде du или dutree всего лишь считают используемые блоки, не учитывая, что некоторые из них могут быть разделяемыми, поэтому они покажут больше занятого места, чем на самом деле используется.

Аналогичным образом, в случае BTRFS стоит избегать команды df , поскольку пространство, занятое файловой системой BTRFS, она покажет как свободное. Лучше пользоваться btrfs filesystem usage :


К сожалению, я не знаю простых способов отслеживания занятого пространства отдельными файлами в файловых системах с COW. На уровне подтома с помощью утилит вроде btrfs-du мы можем получить приблизительное представление о количестве данных, которые уникальны для снапшота и которые разделяются между снапшотами.

Читайте также: