Виртуальная файловая система в Linux - зачем она и как ее использовать

Основы файловой системы

Что такое файловая система? По словам раннего участника и автора Linux, Роберта Лава, «файловая система - это иерархическое хранилище данных, привязанных к определенной структуре». Однако это описание в равной степени применимо к VFAT (виртуальная таблица размещения файлов), Git и Cassandra (база данных NoSQL). Так какая же отличительная черта у файловой системы?

Ядро Linux требует, чтобы программная сущность была файловой системой, которая должна реализовывать методы open(), read() и write() для постоянных объектов, имена которых связаны с ними. С точки зрения объектно-ориентированного программирования ядро рассматривает общую файловую систему как абстрактный интерфейс, и эти большие три функции являются «виртуальными» без определения по умолчанию. Соответственно, стандартная реализация файловой системы ядра называется виртуальной файловой системой (VFS).

VFS подчеркивает известное наблюдение, согласно которому в Unix-подобных системах «все является файлом». Подумайте, как странно, что крошечное демо из примера выше с символьным устройством /dev/console на самом деле работает. Изображение показывает интерактивный сеанс Bash по виртуальному телетайпу (tty). При отправке строки в устройство виртуальной консоли она появляется на виртуальном экране. VFS имеет другие, даже более странные свойства.

Все известные файловые системы, такие как ext4, NFS и /proc, предоставляют определения функций большой тройки в структуре данных на языке C, называемой file_operations. Кроме того, определенные файловые системы расширяют и переопределяют функции VFS знакомым объектно-ориентированным способом. Как указывает Роберт Лав, абстракция VFS позволяет пользователям Linux безболезненно копировать файлы в и из сторонних операционных систем или абстрактных объектов, таких как каналы, не беспокоясь о своем внутреннем формате данных. От имени пользовательского пространства с помощью системного вызова процесс может копировать из файла в структуры данных ядра с помощью метода read() одной файловой системы, а затем использовать метод write() другого типа файловой системы для вывода данных.

Определения функций, которые принадлежат самому базовому типу VFS, находятся в файлах fs/*.c в исходном коде ядра, в то время как подкаталоги fs/ содержат определенные файловые системы. Ядро также содержит объекты, подобные файловой системе, такие как cgroups, / dev и tmpfs, которые необходимы на ранних этапах процесса загрузки и поэтому определяются в подкаталоге init/ ядра. Обратите внимание, что cgroups, / dev и tmpfs не вызывают функции больших трех file_operations, а вместо этого непосредственно читают и записывают в память.

Диаграмма ниже примерно иллюстрирует, как пользовательское пространство обращается к различным типам файловых систем, обычно монтируемых в системах Linux. Не показаны такие конструкции, как pipe, dmesg и часы POSIX, которые также реализуют struct file_operations и доступ к которым, следовательно, проходит через слой VFS.

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

/tmp: простой совет

Простой способ выяснить, какие VFS присутствуют в системе, это набрать команду mount | grep -v sd | grep -v :/, в которой перечислены все смонтированные файловые системы, которые не находятся на диске и не являются NFS на большинстве компьютеров. Одно из перечисленных монтирований VFS наверняка будет /tmp, верно?

Почему хранить /tmp на хранилище нецелесообразно? Поскольку файлы в /tmp являются временными (!), а устройства хранения работают медленнее, чем память, где создаются tmpfs. Кроме того, физические устройства более подвержены износу при частой записи, чем память. Наконец, файлы в /tmp могут содержать конфиденциальную информацию, поэтому возможность их исчезновения при каждой перезагрузке неизбежна.

К сожалению, сценарии установки для некоторых дистрибутивов Linux по-прежнему создают /tmp на устройстве хранения. Не отчаивайтесь, если это произойдет с вашей системой. Следуйте простым инструкциям на великолепной Arch Wiki, чтобы решить проблему, помня, что память, выделенная для tmpfs, не доступна для других целей. Другими словами, система с гигантскими tmpfs с большими файлами в ней может исчерпать память и зависнуть. Другой совет: при редактировании файла /etc/fstab обязательно заканчивайте его новой строкой, иначе ваша система не загрузится (угадайте, откуда я это знаю).

Отслеживание VFS с помощью инструментов eBPF и bcc

Самый простой способ узнать, как ядро ​​управляет файлами sysfs, – это посмотреть его в действии, а самый простой способ посмотреть на ARM64 или x86_64 – это использовать eBPF. eBPF (расширенный пакетный фильтр Berkeley) состоит из виртуальной машины, работающей внутри ядра, которую привилегированные пользователи могут запрашивать из командной строки. Источник ядра сообщает читателю, что может сделать ядро; запуск инструментов eBPF в загруженной системе показывает, что фактически делает ядро.

К счастью, начать работу с eBPF довольно легко с помощью инструментов bcc, которые доступны в виде пакетов из основных дистрибутивов Linux и подробно документированы Бренданом Греггом. Инструменты bcc – это скрипты Python с небольшими встроенными фрагментами C, что означает, что любой, кто знаком с любым языком, может легко их изменить. На этот счет в bcc /tools можно найти около 80 скриптов Python, поэтому весьма вероятно, что системный администратор или разработчик найдет там что-нибудь, соответствующее его потребностям.

Чтобы получить очень грубое представление о том, какую работу выполняют VFS в работающей системе, попробуйте простой vfscount или vfsstat, которые показывают, что каждую секунду происходят десятки вызовов vfs_open() и его друзей.

Для менее тривиального примера давайте посмотрим, что происходит в sysfs, когда USB-карта вставлена в работающую систему.

В первом простом примере, приведенном выше, скрипт bcc инструментов trace.py выводит сообщение всякий раз, когда запускается команда sysfs_create_files(). Мы видим, что sysfs_create_files() был запущен потоком kworker в ответ на вставку USB-накопителя, но какой файл был создан? Второй пример иллюстрирует полную мощность eBPF. Здесь trace.py печатает обратную трассировку ядра (опция -K) плюс имя файла, созданного sysfs_create_files(). Фрагмент внутри одинарных кавычек – это некоторый исходный код на C, включая легко распознаваемую строку формата, которую предоставленный скрипт Python побуждает компилятор LLVM точно в срок компилировать и выполнять внутри виртуальной машины в ядре. Полная сигнатура функции sysfs_create_files() должна быть воспроизведена во второй команде, чтобы строка формата могла ссылаться на один из параметров. Ошибки в этом фрагменте C приводят к распознаваемым ошибкам C-компилятора. Например, если параметр -I опущен, результатом будет "Failed to compile BPF text". Разработчики, знакомые с C или Python, сразу поймут, что инструменты bcc легко расширять и модифицировать.

Когда USB-накопитель вставлен, появляется обратная трассировка ядра, показывающая, что PID 7711 является потоком kworker, который создал файл с именем "events" в sysfs. Соответствующий вызов sysfs_remove_files() показывает, что удаление USB-флешки приводит к удалению файла событий в соответствии с идеей подсчета ссылок. Просмотр sysfs_create_link() с eBPF во время вставки USB-накопителя (не показан) свидетельствует, что создано не менее 48 символических ссылок.

в любом случае какова цель файла событий? Использование cscope для поиска функции __device_add_disk() показывает, что она вызывает disk_add_events(), и в файл событий можно записать либо "media_change", либо "eject_request". Здесь блочный уровень ядра информирует пространство пользователя о появлении и исчезновении "disk". Подумайте, насколько быстро этот метод исследования того, как работает вставка USB-накопителя, сравнивается с попыткой выяснить процесс исключительно из источника.

Наверх
Меню