Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #113, МАЙ 2008 г.

Во власти суперблока

Денис Колисниченко (dhsilabs@mail.ru, www.dkws.org.ua)

Хакер, номер #113, стр. 113-096-1

Файловая система Linux в подробностях

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

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

Прежде, чем приступить к рассмотрению архитектуры файловой системы Linux, нужно ответить на вопрос, что такое файловая система (ФС). В природе можно встретить различные определения, например, ФС – это способ представления информации на носителе данных. Или ФС – часть операционной системы, обеспечивающая выполнение операций над файлами. Но более точным будет объединение этих двух определений. Ведь, грубо говоря, любая файловая система состоит, минимум, из двух уровней – уровня представления данных и набора системных вызовов для работы с данными.

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

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

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

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

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

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

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

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

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

struct super_block {

struct_head s_list; // двусвязный список всех смонтированных ФС

unsigned long s_blocksize;

struct file_system_type *s_type;

struct super_operations *s_op;

struct semaphore s_lock;

int s_need_sync_fs;

...

}

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

Каждому файлу соответствует один и только один индексный дескриптор (inode), который идентифицируется своим порядковым номером – индексом файла. В индексном дескрипторе хранятся метаданные файла. Среди них – все атрибуты файла, кроме его имени, и указатель на данные файла.

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

Сколько блоков с данными можно так адресовать? Адрес блока занимает 4 байта, блок, как уже сказано, имеет размеры 1, 2 или 4 Кб. Значит, путем косвенной адресации можно разместить 256 – 1024 блока.

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

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

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

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

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

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

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

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

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

# mount –t тип_ФС устройство точка_монтирования

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

struct vfsmount {

struct list_head mnt_hash;

struct vfsmount *mnt_parent;

struct dentry *mnt_mountpoint;

struct dentry *mnt_root;

struct super_block *mnt_sb;

struct list_head mnt_mounts;

struct list_head mnt_child;

atomic_t mnt_count;

int mnt_flags;

char *mnt_devname;

struct list_head mnt_list;

}

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

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

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

# dd if=/dev/zero of=fs.img bs=1k count=30000

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

Командой losetup превратим наш файл в блочное устройство:

# losetup /dev/loop0 fs.img

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

Создадим файловую систему на устройстве /dev/loop0 с помощью команды mke2fs:

# mke2fs -c /dev/loop0 30000

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

# mkdir /mnt/fs

# mount -t ext2 /dev/loop0 /mnt/fs

После этого можно работать с каталогом /mnt/fs, как с обычным каталогом файловой системы. Никаких ограничений нет. Внутри этого каталога мы можем создать еще один пустой файл и тоже превратить его в файловую систему, а затем подмонтировать.

Для упрощения процесса вместо команды losetup можно применить параметр '-o loop' команды mount, например:

# mount -o loop -t ext2 fs.img /mnt/fs

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

# umount /mnt/fs

Далее следует удалить устройство /dev/loop0:

# losetup -d /dev/loop0

Alcohol и UltraISO средствами Linux!

Теперь немного практики. Например, dd можно использовать не только для создания пустых файлов, но и для снятия образов с CD/DVD-дисков:

# dd if=/dev/cdrom of=~/image.iso

Данная команда создаст образ CD-диска и запишет его в домашний каталог пользователя под именем image.iso.

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

# mount -o loop -t iso9660 ~/image.iso /mnt/fs

После этого ты можешь работать с /mnt/fs, как с обычным каталогом.

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

# mkdir /swap

# dd if=/dev/zero of=/swap/sw-file bs=1k count=262144

# mkswap /swap/sw-file 262144

# swapon /swap/sw-file

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

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

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

# mount --bind исходный_каталог каталог_назначения

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

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

# chroot точка_монтирования

Например:

# chroot /mnt/newroot

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

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

# mkdir -p /old/dev

# mount /dev/sdaX /old

# mount --bind /dev /old/dev

# chroot /old

# /sbin/grub-install /dev/sda

# reboot

Вместо заключения

К сожалению, мы рассмотрели далеко не все возможности файловой системы Linux. В частности, не были рассмотрены права доступа, квотирование, практически ничего не сказано о создании ссылок. Все это темы для отдельного разговора, а пока, если у тебя есть вопросы/комментарии/пожелания, можешь обращаться по адресу dhsilabs@mail.ru.

INFO

  • VFS действует как корневой уровень интерфейса файловой системы. VFS следит за всеми поддерживаемыми и смонтированными на данный момент файловыми системами.
  • Узел inode хранит в себе все метаданные для управления объектами файловой системы.
  • Cтруктуры dentry используются для осуществления преобразования между названиями и узлами inode, для чего существует кэш директорий, в котором хранятся последние использованные записи, а также отношения между папками и файлами для обхода файловых систем.
  • Буферный кэш буферизирует запросы между файловыми системами и блочными устройствами.
  • Дубликаты суперблока используются при восстановлении файловой системы после сбоев.
  • В статье «Через революцию к эволюции», опубликованной в журнале ][акер #095, можно прочитать историю создания линуксовых файловых систем.

WWW

На Википедии (ru.wikipedia.org) можно найти кучу материалов, посвященных файловым системам. Для поиска используй ключевые слова: ext2, ext3, reiserfs, fuse, sshfs.

Содержание