20. Лекция: Виртуальные файловые системы (vfs). Реализации файловых систем. Сетевая файловая система nfs
В лекции рассмотрены следующие вопросы: виртуальные файловые системы (VFS); реализация файлов с помощью FAT (Windows) и индексных блоков (UNIX); управление внешней памятью; кэширование; файловые системы на основе журнала транзакций; сетевая файловая система NFS.
Виртуальные файловые системы
Методы размещения файлов
Файловые системы, основанные на расширениях
Ссылочное размещение файла
Управление свободной внешней памятью
Эффективность и производительность дисковой памяти
Проблемы восстановления файлов
Сетевая файловая система NFS
Набор для практики
Темы для курсовых работ, рефератов, эссе
Значительную роль для пользователей в современных условиях играют виртуальные и сетевые файловые системы, которые позволяют осуществлять операции над файлами в удобных терминах на должном уровне абстракции. Весьма важны также эффективные и надежные методы реализации файлов и операций над ними в файловых системах. Все эти вопросы рассмотрены в данной лекции.
Виртуальные файловые системы
Виртуальные файловые системы (VFS) обеспечивают объектно-ориентированный способ реализации файловых систем.
VFS обеспечивает единый интерфейс системных вызовов (API) для различных типов файловых систем, которые могут быть очень разными по своей реализации, включая сетевые файловые системы.
Данный API является набором операций над самой VFS, а не над каким-либо специфическим типом файловых систем.
Схема организации виртуальной файловой системы изображена на рис. 20.1.

Рис. 20.1. Схема организации виртуальной файловой системы.
Реализация директорий
Директория – центральная системная структура в файловой системе, на которой основан поиск файлов. Поэтому эффективная реализация директорий особенно важна.
Наиболее простой метод реализации директорий — линейный список имен с указателями на блоки данных. Такая реализация просто программируется, однако требует большого времени выполнения.
Более эффективный метод реализации — хэш-таблица – линейный список с хэш-оглавлением и подразделением на (небольшие) списки элементов с одним и тем же значением хеш-функции. Такой метод уменьшает время поиска в директории.
При использовании хеш-таблицы возможны коллизии – ситуации, когда два имени хэшируются в один и тот же адрес. Данная проблема решается использованием метода цепочек – представления хеш-таблицы, при котором для каждого значения хеш-функции хранится список элементов, имеющих данное значение хеш-функции.
Методы размещения файлов
Смежное размещение. Термин метод размещения означает метод, с помощью которого размещаются блоки файла во внешней памяти. Различаются следующие основные методы размещения файлов:
При смежном размещении каждый файл занимает набор смежных блоков на диске. Преимущество данного метода — простота: требуется хранить только одну ссылку (номер блока) и длину (число блоков). Другим преимуществом является возможность произвольного доступа.
Недостатки данного метода следующие:
возможны потери дисковой памяти из-за фрагментации (метод аналогичен общей задаче динамического распределения памяти, рассмотренной ранее);
невозможность увеличения размера файла.
Смежное размещение файлов иллюстрируется на рис. 20.2.

Рис. 20.2. Смежное размещение файлов.
Файловые системы, основанные на расширениях
Многие современные файловые системы, — например, Veritas File System, или Vx-FS – основная файловая система в ОС HP-UX фирмы Hewlett-Packard, — используют модифицированное смежное размещение файлов. Дисковые блоки в такой системе размещаются в расширениях (extents). Расширение – это смежный блок на диске. Файл состоит из одного или нескольких расширений. Таким образом, если длина файла не увеличивается, он хранится в виде одной смежной области внешней памяти, что обеспечивает максимальную эффективность доступа. В случае увеличения длины файл представляется списком из основной части и расширений.
Ссылочное размещение файла
При ссылочном размещении каждый файл представляется в виде связанного списка дисковых блоков, которые могут быть разбросаны по диску. Преимущества данного метода:
Простота – необходимо хранить только начальный адрес;
Отсутствие потерь дискового пространства; система хранит списки свободной памяти.
Недостаток — отсутствие произвольного доступа: для доступа к данным файла в общем случае необходимо выполнить просмотр части списка блоков файла.
При ссылочном размещении адрес по файлу представляется в виде (Q, R), где Q – номер блока, к которому выполняется доступ, в связанном списке блоков, представляющем файл; R — смещение в блоке.
Ссылочное размещение файлов изображено на рис. 20.3.

Рис. 20.3. Ссылочное размещение файлов.
К файловым системам, использующим ссылочное размещение, относится файловая система File-allocation table (FAT), используемая в MS-DOS и OS/2 и до сих пор используемая в Windows (рис. 20.4).

Рис. 20.4. Файловая система File Allocation Table (FAT).
В системе FAT элемент директории содержит имя файла и адрес его начального блока (кластера) на диске. Каждый блок ссылается на следующий, кроме последнего. Система FAT разработана создателем корпорации Microsoft Биллом Гейтсом в 1976 г. Теперь читателю должно быть понятно, почему операция удаления файла в Windows не «фатальна»: она означает лишь удаление элемента директории со ссылкой на начальный кластер файла. Несмотря на это, список кластеров, представляющий файл, можно обнаружить и восстановить ссылку из директории на его начало, хотя и под другим именем. В MS DOS данную функцию выполняла системная утилита unerase (отменить удаление).
Индексируемое размещение
При индексируемом размещении, в отличие от предыдущих, все указатели на блоки файла собраны вместе в индексный блок (i-node, superblock). Используется индексная таблица, ссылающаяся на блоки данных файла. Подобная система используется в системах UNIX, Linux, Solaris.
Пример индексируемого размещения приведен на рис. 20.5.

Рис. 20.5. Индексируемое размещение файлов.
Как видно из схемы, при индексируемом размещении блоки файла могут быть расположены как угодно разрозненно, но индексный блок содержит все ссылки на них. Ссылка на блок данных может быть выбрана непосредственно из индексного блока, без какого-либо поиска.
Определенная опасность такого размещения в том, что на индексный блок ложится критическая нагрузка: если его целостность будет нарушена, файл восстановлению не подлежит. Именно поэтому в UNIX команда rm, удаляющая файл, «фатальна» для него.
Таким образом, при индексируемом размещении файлов необходима индексная таблица. Преимущество такого метода размещения — возможность произвольного доступа; отсутствие внешней фрагментации. Накладными расходами является индексный блок.
При отображении логического адреса в физический, если ограничить максимальный размер файла 256 K словами, а размер блока — 512 слов, то для индексной таблицы требуется только один блок. Логический адрес будет иметь вид (Q, R), где Q — смещение в индексной таблице, R — смещение в блоке.
При отображении логического адреса в физический для файла неограниченной длины (при размере блока – 512 слов) может использоваться ссылочная схема – в список связываются блоки индексной таблицы. В данном случае логический адрес будет иметь вид (Q1, R1), где Q1 = номер блока индексной таблицы; R1 = (Q2, R2), где Q2 — смещение в блоке индексной таблицы, R2 смещение в блоке файла.
В системе UNIX используется комбинированная схема индексного размещения файлов: возможна одноуровневая адресация данных через индексные блоки, двухуровневая (индексные блоки адресуют другие индексные блоки, а те, в свою очередь, — блоки данных), трехуровневая и т.д. Данная схема иллюстрируется на рис. 20.6.

Рис. 20.6. Комбинированная индексная схема размещения файлов в UNIX.
Управление свободной внешней памятью
Для управления свободной памятью на диске система использует битовый вектор длины n, где n – общее число блоков на диске, каждый бит которого описывает состояние соответствующего блока: bit[i] = 0, если block[i] свободен, 1 – если блок занят. При таком представлении информации о свободных и занятых блоках, номер первого занятого блока вычисляется по простой формуле:
номер первого занятого блока = число битов в слове * число нулевых слов + номер первой 1 .
Битовые шкалы, используемые для управления блоками, требуют дополнительной памяти. Например, при размере блока в 2 12 байтов и размере диска в 2 30 байтов (1 GB) длина битового вектора будет равна n = 2 30 /2 12 = 2 18 битов (или 32 KB).
При использовании битовых векторов легко получать информацию о смежно расположенных файлах. Для сравнения, при использовании связанного списка свободной дисковой памяти (метод, обычно применяемый в ОС для оперативной памяти), невозможно легко получить информацию о смежных областях памяти, но зато нет лишнего расходования памяти.
Для предотвращения ситуаций рассогласованности информации о свободной дисковой памяти, операционной системе необходимо защищать указатель на список свободной памяти, либо битовую шкалу (в зависимости от способа представления информации о свободной памяти). Битовый вектор должен храниться на диске. Однако его копии в памяти и на диске могут различаться. Нельзя допустить, чтобы block[i] имел такую ситуацию, когда bit[i] = 1 в памяти и bit[i] = 0 на диске. Решение этой проблемы в том, чтоты согласовывать значения битов на диске и в памяти по следующему алгоритму:
Установить bit[i] = 1 на диске;
Установить bit[i] = 1 в памяти.
Представление информации о свободной дисковой памяти в виде списка блоков иллюстрируется на рис. 20.7.

Рис. 20.7. Список свободной дисковой памяти.
Эффективность и производительность дисковой памяти
Эффективность использования дисковой памяти зависит от:
Алгоритмов распределения дисковой памяти и управления директориями;
Типов данных, хранимых в элементе директории для файла.
Для повышения производительности работы с диском используются следующие методы:
Кэширование диска – использование специальной области основной памяти для часто используемых блоков диска;
Освобождение прочитанного (free-behind) и опережающее считывание (read-ahead) – методы оптимизации последовательного доступа к диску, которые заключаются в том, что основная память, в которой хранились копии прочитанных блоков, освобождается, а вместе с очередным блоком файла считываются в основную память и несколько следующих блоков;
Организация виртуальных дисков — улучшение производительности ПК путем выделения области памяти под виртуальный диск (RAM-диск).
Кэширование диска
Буферная кэш-память. В операционных системах используются различные методы кэширования диска (рис. 20.8).

Рис. 20.8. Различные методы размещения кэша для диска.
В основной памяти хранятся: виртуальные диски, буфера блоков обрабатываемых файлов, таблицы открытых файлов. В буферной памяти контроллера диска хранятся копии обрабатываемых блоков файла.
Проблема в том, что различные модули кэш-памяти используются не всегда достаточно эффективно и не всегда достаточно согласованы друг с другом. Например, система поддерживает кэш страниц, который кэширует страницы, а не блоки файла, используя методы организации виртуальной памяти. Ввод-вывод файлов, отображаемых в память, использует кэш страниц. С другой стороны, обычный ввод-вывод через файловую систему использует кэш буфера (диска). Понятно, что эти различные кэш-модули решают сходные задачи. Не хватает универсализации кэш-памяти. В результате получается усложненная схема, приведенная на рис. 20.9.

Рис. 20.9. Ввод-вывод без унифицированной буферной кэш-памяти.
Решение данной проблемы – использование унифицированной буферной кэш-памяти. Унифицированная буферная кэш-память использует один и тот же кэш страниц для кэширования и файлов, отображаемых в память, и обычных операций ввода-вывода через файловую систему. Схема ввода-вывода с использованием унифицированной буферной кэш-памяти изображена на рис. 20.10.

Рис. 20.10. Ввод-вывод с использованием унифицированной буферной кэш-памяти.
Проблемы восстановления файлов
Файловые системы с журналом транзакций. При работе с файловыми системами и с дисками возможны различные экстремальные ситуации – от сбоя диска до выключения питания. Подобные ситуации могут привести к порче информации в файле или к несогласованности информации в файле и в его буферах в основной памяти. Для обнаружения и диагностики таких проблем в операционных системах имеются утилиты проверки согласованности, которые сравнивают данные в структуре директорий с блоками данных на диске и пытаются исправить несогласованности.
Еще раз повторим, что наилучший способ сохранить свои файлы – это регулярно использовать системные программы для резервного копирования (back up) данных с диска на другое устройство памяти (стример, флэшку, внешний жесткий диск, и т.д.). Восстановить испорченный файл на диске можно с помощью его резервной копии. Автор сам на себе недавно испытал ситуацию, когда по непонятной причине (видимо, из-за сбоя диска, которые случаются крайне редко) оказался испорченным файл с главой книги «Trustworthy Compilers», подготовленной для издательства John Wiley & Sons, причем копия файла на флэшке также оказалась испорченной. Проблему решила заранее предусмотрительно сделанная копия файла на вторую флэшку.
Более систематический подход к поддержанию целостности и отслеживанию состояний файлов предпринят в файловых системах с журналом транзакций (log structured filesystems). Файловые системы с журналом фиксируют любое изменение в файловой системе как транзакцию (по аналогии с системами управления базами данных и современными web-сервисами). Все транзакции записываются в журнал. Транзакция считается одобренной (committed), если она записана в журнал. Однако файловая система может быть в этот момент еще не обновлена. Транзакции из журнала асинхронно выполняются над файловой системой. Когда файловая система модифицируется, транзакция удаляется из журнала. Если имеет место сбой файловой системы, то все оставшиеся транзакции из журнала, тем не менее, должны быть выполнены.
Сетевая файловая система NFS
Система NFS (Network File System) —одна из наиболее распространенных сетевых файловых систем, разработанная фирмой Sun Microsystems и используемая в системе Solaris.
NFS – это спецификация и реализация программной системы для доступа к удаленным файлам через локальную (или глобальную) сеть.
Данная реализация является частью операционных систем Solaris и SunOS, использующих быстрый, но менее надежный асинхронный сетевой протокол, основанный на датаграммах (UDP/IP), и сеть Ethernet (сетевые протоколы подробно рассматриваются далее в соответствующих лекциях курса).
В реализации NFS используются процессы-демоны nfsd (обработка NFS-запросов клиентов) и mountd (обработка запросов монтирования), а также biod (асинхронный ввод-вывод блоков удаленных файлов на клиенте).
Взаимодействующие рабочие станции рассматриваются в NFS как набор независимых машин с независимыми файловыми системами, что позволяет совместно использовать файловые системы прозрачным образом.
Удаленная директория монтируется на локальную директорию. Смонтированная директория трактуется как полное поддерево локальной файловой системы, заменяя поддерево локальной файловой системы.
Спецификация удаленной директории для операции монтирования не является прозрачной; необходимо указать имя машины с удаленной директорией. После этого файлы в удаленной директории могут быть доступны прозрачным образом, т.е. операции над локальными и удаленными файлами программируются одинаково. При этом выполняется доступ только к тем блокам файла, к которым реально происходит обращение, а не ко всему файлу.
С учетом назначения полномочий доступа, потенциально любая файловая система (или директория внутри файловой системы), может быть смонтирована удаленно поверх локальной директории.
NFS спроектирована для работы в неоднородном окружении различных машин, ОС и сетевых архитектур; спецификация NFS от них не зависит.
Эта независимость достигнута благодаря использованию примитивов RPC (Remote Procedure Call – удаленный вызов процедуры), реализованных поверх External Data Representation (XDR) – протокола – машинно-независимого представления данных для их передачи через сеть.
Спецификация NFS различает сервисы, обеспечиваемые механизмом монтирования, и фактические удаленные файловые системы.
Протокол монтирования в NFS устанавливает первоначальную логическую связь между сервером и клиентом.
Операция монтирования включает имя удаленной директории, подлежащей монтированию, и имя машины-сервера, на которой она хранится.
Запрос на монтирование отображается на соответствующий RPC и передается на mount-сервер, исполняемый на серверной машине.
Список экспорта (export list) указывает список файловых систем, которые сервер экспортирует для монтирования, а также имена машин, на которых разрешено их монтировать.
Исполняя запрос на монтирование, соответствующий этому списку, сервер возвращает обработчик файла (file handle)—ключ к дальнейшему доступу. File handle содержит идентификатор файловой системы и номер inode (индексного узла), идентифицирующий монтируемую директорию внутри экспортируемой файловой системы.
Операция монтирования изменяет только точку зрения клиента и не влияет на серверную часть.
Монтирование в NFS иллюстрируется рис. 20.11 и рис. 20.12: на рис. 20.11 изображены три независимых файловых системы, а на рис. 20.12 – результат монтирования в NFS одной из них (директория dir1 смонтировалась как поддиректория удаленной файловой системы /usr/local.

Рис. 20.11. Три независимых файловых системы.

Рис. 20.12. Монтирование в NFS.
Протокол NFS предоставляет набор RPC для удаленных операций над файлами. Процедуры поддерживают следующие операции:
Поиск файла в директории
Чтение набора элементов директории
Управление ссылками и директориями
Доступ к атрибутам файлов
Чтение и запись файлов
NFS – серверы не имеют состояния (stateless); каждый запрос должен иметь полный набор аргументов.
Модифицированные данные должны быть направлены на диск сервера до того, как результаты вернутся к клиенту (теряется возможность кэширования).
NFS – протокол не поддерживает механизмы управления параллельным доступом.
Система NFS имеет три основных уровня архитектуры:
UNIX – интерфейс файловой системы (основан на вызовах open, read, write и close calls и на дескрипторах файлов).
Уровень Virtual File System (VFS) – различает локальные и удаленные файлы, и в дальнейшем локальные файлы обрабатываются в соответствии с типами их файловых систем. VFS активизирует операции, специфичные для конкретной файловой системы, для обработки локальных запросов в соответствии с типами файловых систем. Уровень VFS вызывает процедуры NFS – протокола для удаленных запросов.
NFS – сервисный уровень – нижний уровень архитектуры; реализует NFS – протокол.
Схема архитектуры NFS приведена на рис. 20.13.

Рис. 20.13. Схема архитектуры NFS.
Трансляция имен путей в NFS выполняется путем разбивки имени пути на последовательность имен компонент и выполнения отдельного NFS — поиска для каждой пары (компонента, vnode директории).
Для ускорения поиска на клиентской машине организуется кэш имен удаленных директорий.
Удаленные операции NFS обеспечивают почти взаимно-однозначное соответствие между обычными системными вызовами UNIX и удаленными вызовами процедур протокола NFS (за исключением открытия и закрытия файлов).
NFS основана на парадигме удаленных сервисов, но поддерживает буферизацию и кэширование для повышения эффективности.
NFS организует кэш блоков файлов: когда файл открывается, ядро проверяет у удаленного сервера, необходимо ли обновить кэшируемые атрибуты. Кэшируемые блоки используются, только если соответствующие кэшированные атрибуты самые свежие. Кэш атрибутов файлов обновляется по мере передачи обновленных атрибутов с сервера.
Клиенты не освобождают задерживаемые блоки до тех пор, пока сервер не подтвердит, что они записаны на диск.
Выражаясь менее формально, впечатления от многолетней работы автора в локальной сети с использованием NFS достаточно благоприятные. Система обеспечивает монтирование удаленных файловых систем и работу с ними, как если бы они были расположены на локальной машине (только, разумеется, с некоторым замедлением). Она достаточно удобна и надежна. При каких-либо сбоях характерной чертой NFS является то, что она оставляет в локальных директориях временные файлы – копии удаленных файлов – с именами вида .N0035 и т.д. В таком случае рекомендуется удалить эти файлы обычным образом и продолжить работу.
Виртуальная файловая система
Виртуальная файловая система (англ. virtual file system — VFS) или виртуальный коммутатор файловой системы (англ. virtual filesystem switch ) — уровень абстракции поверх конкретной реализации файловой системы. Целью VFS является обеспечение единообразного доступа клиентских приложений к различным типам файловых систем. VFS может быть использована, например, для прозрачного доступа к локальным и сетевым устройствам хранения данных без использования специального клиентского приложения (независимо от типа файловой системы). VFS определяет интерфейс между ядром и конкретной файловой системой, таким образом, можно легко добавлять поддержку новых типов файловых систем, внося изменения только в ядро операционной системы.
Иногда виртуальными файловыми системами называют псевдо-файловые системы, которые не предназначены для хранения данных. Примером такой системы является procfs. [1]
Содержание
Реализации
Одна из первых виртуальных файловых систем в Unix-подобных ОС была реализована Sun Microsystems в SunOS 2.0 в 1985 году. Это позволило системным вызовам Unix получить прозрачный доступ к локальной UFS и удалённой NFS. По этой причине, вендоры Unix-систем, получившие лицензию на код NFS, часто копировали дизайн VFS от Sun. Другие файловые системы могут быть подключены также: появилась реализация файловой системы MS-DOS FAT, разработанная в Sun, использовавшая SunOS VFS, хотя она не поставлялась в качестве продукта до SunOS 4.1. Реализация SunOS лежит в основе механизма VFS в System V Release 4.
Джон Хейдеманн ( John Heidemann ) разработал стековую VFS под SunOS 4.0 для экспериментальной файловой системы Ficus . Этот проект предусматривал повторное использование кода в файловых системах с различной, но похожей семантикой (например, шифрованная файловая система может использовать систему имён и код для хранения данных нешифрованной файловой системы). В своей диссертации Хейдеманн адаптировал эту работу для использования в 4.4BSD. Наследники этого кода лежат в основе реализаций современных файловых систем в операционных системах, производных от BSD, включая Mac OS X.
Другие виртуальные файловые системы Unix включают File System Switch в System V Release 3, Generic File System в Ultrix и VFS в Linux.
В OS/2 и Microsoft Windows механизм виртуальной файловой системы называется устанавливаемой файловой системой ( Installable File System ).
Файловая система в пользовательском пространстве ( Filesystem in Userspace — FUSE) позволяет программам реализовать файловую систему в пользовательском пространстве через подключение к виртуальной файловой системе. FUSE используется в Linux, FreeBSD, OpenSolaris, и Mac OS X.
Виртуальные файловые системы
Что такое файловая система? Согласно высказыванию одного из первых мэйнтейнеров Linux и автора популярных книг — Роберта Лава, файловая система — это иерархическое хранилище данных, придерживающиеся определённой структуры.
Однако, это описание одинаково хорошо подходит как для VFAT (Virtual File Allocation Table), так и для Git, Cassandra и других баз данных. Так что же отличает файловую систему? В этой статье мы попытаемся ответить на этот вопрос, а также разобраться что такое виртуальные файловые системы.
Основы файловых систем
Ядро Linux требует, чтобы во всём, что считается файловой системой были реализованы методы open(), read(), и write() для постоянных объектов, у которых есть имена. С точки зрения объективно ориентированного программирования, ядро считает файловую систему абстрактным интерфейсом, в котором определены эти виртуальные функции без реализации. Таким образом реализация файловой системы на уровне ядра называется VFS (Virtual Filesystem).
Мы можем открыть, прочитать и записать в файл.
Термин VFS лежит в основе всем известного утверждения о том, что в Unix-подобных системах всё является файлом. Подумайте о том, насколько странно, что приведенная выше последовательность действий с файлом /dev/console работает. На снимке показан интерактивный сеанс Bash в виртуальном терминале TTY. При отправке строки устройству виртуальной консоли, она появляется на виртуальном экране. VFS имеет и другие, даже более странные свойства. Например, в таких файлах можно выполнять поиск.
В таких популярных файловых системах как Ext4, NFS и даже в подсистеме /proc реализованы три основные функции в структуре данных на языке Си, которая называется file_operations. Кроме того, некоторые файловые системы расширяют и переопределяют функции VFS подобным объективно ориентированным способом. Как утверждает Роберт Лав, абстракция VFS позволяет пользователям Linux копировать файлы из других операционных систем или абстрактных объектов, таких как каналы не беспокоясь об их внутреннем формате данных. В пространстве пользователя с помощью системного вызова read() процессы могут копировать содержимое файла в структуры ядра из одной файловой системы, а затем использовать системный вызов write() в другой файловой системе, чтобы записать полученные данные в файл.
Определения функций, относящиеся к VFS находятся в файлах fs/*.c в исходном коде ядра. Подкаталоги fs/ же содержат различные файловые системы. Ядро также содержит объекты, похожие на файловые системы, это cgroups, /dev и tmpfs, которые нужны на раннем этапе загрузки системы и поэтому определены в подкаталоге исходников init/. Обратите внимание, что они не вызывают функции большой тройки из file_operations, зато они могут непосредственно читать и записывать в память.
На схеме ниже наглядно показано, как из пространства пользователя можно получить доступ к большинству файловых систем. На рисунке нет каналов, таймера POSIX и dmesg, но они тоже используют методы из структуры file_operations и работают через VFS:

Существование VFS способствует повторному использованию кода, поскольку основные методы для работы с файловыми системами не переопределяются в каждой файловой системе. Это широко используемая практика, однако если в таком коде есть ошибки, то от них страдают все реализации, использующие общие методы.
Самый простой способ вывести все виртуальные файловые системы, это выполнить такую команду:
mount | grep -v sd | grep -v :/
Она выведет все смонтированные файловые системы, которые не связанны с физическим или сетевым диском. Оной из первых точек монтирования виртуальных файловых систем будет /tmp. Так почему не рекомендуется хранить содержимое /tmp на диске? Потому что файлы из /tmp временные, а постоянные хранилища намного медленнее памяти, где находится tmpfs. Кроме того, физические устройства более подвержены износу от частой записи, в отличие от оперативной памяти. И наконец, файлы в /tmp могут содержать конфиденциальную информацию, поэтому их лучше удалять при каждой перезагрузке.
К сожалению, установщики некоторых дистрибутивов всё ещё размещают /tmp на физическом диске. Но если такое случилось с вашей системой, не расстраивайтесь. Вы можете использовать инструкцию с ArchWiki чтобы исправить проблему. Но не забывайте, что выделенная под tmpfs память больше ни для чего не может быть использована.
/proc и /sys
Помимо /tmp, виртуальные файловые системы, с которыми знакомо большинство пользователей Linux — это /proc и /sys (/dev полагается на общую память и не поддерживает file_operations). Но зачем аж две? Давайте разбираться.
Файловая система procfs предоставляет моментальный снимок состояния ядра и процессов, которые оно контролирует в пространстве пользователя. Кроме того, в /proc ядро делает доступной информацию о прерываниях, виртуальной памяти и планировщике. А ещё в /proc/sys размещены параметры, которые можно настроить из пространства пользователя с помощью команды sysctl. Состояние и статистика по каждому процессу находятся в директориях /proc/<PID>.
/proc/meminfo — пустой файл, но тем не менее, он содержит информацию о памяти
Поведение файлов в /proc показывает, насколько виртуальные файловые системы могут отличаться от, тех, у которых есть файлы на диске. С одной стороны, файл /etc/meminfo содержит информацию, выводимую командой free. Но с другой стороны, он пустой! Как такое может быть. Ситуация напоминает знаменитую статью, написанную физиком Корнельского университета Дэвидом Мермином в 1985 году под названием Есть ли Луна когда никто не смотрит? На самом деле ядро собирает статистику о памяти, когда процесс запрашивает её из /proc. И на самом деле в файлах из /proc ничего нет, когда никто не смотрит. Как сказал Мермин, «Фундаментальная квантовая доктрина состоит в том, что измерение, как правило, не выявляет ранее существовавшее значение измеряемого свойства.
Кажущаяся пустота /proc имеет смысл, поскольку доступная информация динамическая и актуальная на момент получения. Ситуация с файловой системой /sys. Давайте сравним сколько файлов, размером хотя бы один байт, находятся в /proc и /sys:

В procfs такой файл только один — это конфигурация ядра, которую надо генерировать только один раз во время загрузки. С другой стороны в каталоге /sys содержится много файлов большого размера, но большинство из них составляют одну страницу памяти. Обычно файлы из sysfs содержат одно число или строку, в отличие от таблиц информации, например, из /proc/meminfo.
Цель sysfs — предоставить доступ для чтения и записи свойств так называемых объектов kobjects в пользовательском пространстве. Единственная цель kobject — подсчёт ссылок, когда последняя ссылка на kobject будет удалена, система освободит ресурсы, связанные с ним. К тому же, /sys/ составляет основную часть стабильного ABI ядра для пользовательского пространства, которую никто и ни при каких обстоятельствах не может сломать. Но это не значит, что файлы в sysfs статичны. Это бы противоречило подсчёту ссылок.
Стабильность ABI ограничивает то, что может появится в /sys, а не то, что на самом деле есть в любой конкретный момент. Перечисление разрешений на файлы в sysfs дает представление о том, что настраиваемые параметры устройств, модулей и файловых систем можно прочитать и изменить. По логике procfs тоже должна быть частью стабильного ABI ядра, однако в документации об этом ничего не сказано.
Файлы в sysfs можно читать или записывать или всё вместе. Здесь 0 означает, что SSD не извлекаем.
Слежение за VFS
Самый простой способ узнать как ядро управляет файлами в sysfs — это посмотреть на это всё в действии. А самый простой способ это сделать на ARM64 или x86_64 — это использование eBPF. eBPF (extended Berkeley Packet Filter) — состоит из виртуальной машины, работающей на уровне ядра, к которой привилегированные пользователи могут обращаться из командной строки. Исходный код ядра показывает читателю как ядро может что-то сделать. Инструменты eBPF показывают как на самом деле всё происходит.
К счастью, начать работу с eBPF довольно просто с помощью инструментов bcc, для которых доступны пакеты в множестве дистрибутивов. Инструменты bcc — это скрипты на Python с небольшими фрагментами кода на Си, а это значит, что любой кто знаком с этим языком может их модифицировать. На данный момент существует около 80 скриптов на Python в bcc, поэтому каждый найдёт то, что ему надо.
Чтобы получить общее представление о работе VFS в работающей системе используйте простые скрипты vfscount и vfsstat, которые покажут, что каждую секунду выполняются десятки вызовов vfs_open() и подобных функций:

В качестве менее общего примера, давайте посмотрим что происходит, когда к работающей системе подключается USB накопитель:

В первом примере на этом снимке скрипт trade.py выводит сообщение всякий раз, когда вызывается функция sysfs_create_files(). Вы можете видеть, что эта функция была вызвана процессом kworker после подключения USB, но какой файл был создан? Следующий пример иллюстрирует полную силу eBPF. Скрипт trade.py выводит трассировку вызовов ядра (опция -K), а также имя файла, созданного функцией sysfs_create_files(). Фрагмент в одинарных кавычках — это строка кода на Си, которую Python скрипт компилирует и выполняет внутри виртуальной машины в ядре. Полную сигнатуру функции sysfs_create_files () надо воспроизвести во втором примере чтобы можно было ссылаться на один из её параметров в функции вывода. Ошибки в этом коде вызовут ошибки компиляции.
Когда USB-накопитель вставлен, появляется трассировка вызовов ядра, показывающая что один из потоков kworker с PID 7711 создал файл с именем events в sysfs. При попытке отслеживать вызов sysfs_remove_files() вы увидите, что извлечение флешки приводит к удалению файла events в соответствии с идеей отслеживания ссылок. Отслеживание sysfs_create_link() во время подключения USB накопителя показывает что создается не менее 48 ссылок.
Зачем же нужен файл events? Используя инструмент cscope можно найти функцию __device_add_disk(), которая вызывает функцию disk_add_events(). А та в свою очередь может записать в файл «media_change» или «eject request». Здесь ядро сообщает в пользовательское пространство о появлении или исчезновении диска. Это намного информативнее, чем просто анализ исходников.
Файловые системы только для чтения
Никто не выключает сервер или компьютер, просто отключив питание. Почему? Потому что примонтированные файловые системы на физических устройствах могут выполнять запись и структуры данных файловой системы могут быть не полностью перенесены на диск. Когда такое происходит, вам придется при следующей загрузке подождать восстановления файловой системы с помощью fsck, а в худшем случае вы даже можете потерять данные.
Тем не менее, вы наверное слышали, что многие встраиваемые устройства, такие как маршрутизаторы, термостаты и даже автомобили работают под Linux. У многих из этих устройств нет пользовательского интерфейса и их невозможно корректно выключить. Представьте автомобиль, основной компьютер которого полностью отключается, когда разряжается батарея. Как тогда быстро включить компьютер, когда двигатель снова заработает и батарея восстановит заряд? Ответ прост. IoT устройства используют корневую систему в режиме только для чтения или коротко ro-rootfs.
Такой подход даёт несколько преимуществ, которые менее очевидны чем неповреждаемость. Одна из них — вирусы не могут выполнять запись в /user или /lib если ни один процесс Linux не может выполнять туда запись. Во вторых это обеспечивает удобную поддержку удалённых устройств, поскольку они имеют такую же файловую систему как и тестовые образцы. Самое важное обстоятельство такого подхода — разработчикам приходится решать на этапе проектирования какие компоненты системы должны быть неизменяемыми. Работа с ro-rootfs иногда может быть очень сложной, но преимущества компенсируют недостатки.
Создания таких файловых систем требует дополнительных усилий от разработчиков и здесь снова на помощь приходит VFS. Linux требует, чтобы файлы в /var были доступны для записи, кроме того, множество приложений будут пытаться создавать скрытые файлы в домашней папке. Решением для файлов конфигурации в домашнем каталоге моет быть их предварительное создание. В каталог /var можно монтировать отдельный раздел, доступный для записи, тогда как корень будет все ещё доступен только для чтения. Другая же альтернатива — использование bind и overlay монтирования.
Bind и overlay монтирование
Лучше всего про монтирование файловых систем и про bind монтирование в частности можно узнать выполнив такую команду:
Bind и overlay монтирование позволяет разработчикам встраиваемых систем и системным администраторам создавать файловую систему, доступную по определённому пути, а потом делать её доступной приложениям по совсем другому пути. Для встраиваемых систем подразумевается что можно хранить файлы из /var на устройстве доступном только для чтения, но наложить на /var tmpfs, так чтобы приложения могли туда писать если им это надо. При перезагрузке все изменения в /var будут утеряны. Overlay монтирование позволяет объединить tmpfs и обычную файловую систему так, чтобы в ней можно было изменять файлы, а bind монтирование позволяет сделать доступными для записи пустые папки с tmpfs в ro-rootfs. Хотя overlayfs — это отдельная файловая система, bind монтирование выполняется с помощью VFS.
Исходя из всего этого, не удивительно, что контейнеры Linux активно используют overlay и bind монтирование. Давайте посмотрим что происходит, когда мы запускаем контейнер с помощью systemd-nspawn. Будем использовать инструмент mountsnoop из набора bcc:

Осталось посмотреть что произошло:

Здесь systemd-nspawn размещает нужные файлы из procfs и sysfs хоста в контейнер. Здесь помимо флага MS_BIND, означающего bind-монтирование есть флаги, которые настраивают связь при изменениях файлов процессами контейнера или хоста. Например, файловая система может автоматически отображать изменения в контейнере или скрывать их.
Выводы
Понимание внутреннего устройства Linux может показаться невыполнимой задачей, поскольку само ядро содержит огромный объем кода и это не считая приложений пространства пользователя и библиотеки glibc. Один из способов достичь прогресса — прочитать исходный код одной из подсистем ядра, обращая внимание на системные вызовы, которые используются пространством пользователя, а также на основные внутренние интерфейсы ядра, вроде структуры file_operations.
Структура file_operations — это то, что делает концепцию «Всё есть файл» работающей, поэтому изучение информации о ней особенно интересно. Исходные файлы ядра в каталоге fs/ представляют собой реализацию виртуальных файловых систем, которые представляют из себя слой прокладки для взаимодействия между физическими устройствами и популярными файловыми системами. Bind и overlay монтирование — это магия VFS, которая делает возможным использование Linux на IoT устройствах и контейнеры. Инструменты eBPF вместе с изучением исходного кода ядра, делают исследование ядра проще чем когда либо.
Виртуальные файловые системы в Linux: зачем они нужны и как они работают? Часть 1
Всем привет! Мы продолжаем запуски новых потоков по уже полюбившимся вам курсам и сейчас спешим сообщить о том, что у нас стартует новый набор по курсу «Администратор Linux», который запустится в конце апреля. К этому событию и будет приурочена новая публикация. С оригиналом материала можно ознакомиться тут.
Виртуальные файловые системы выполняют роль некой волшебной абстракции, которая позволяет философии Linux говорить, что «всё является файлом».

Что такое файловая система? Опираясь на слова одного из первых контрибьюторов и авторов Linux Робера Лава, «Файловая система – это иерархическое хранилище данных, собранное в соответствии с определенной структурой». Как бы то ни было, это определение в равной мере хорошо подходит для VFAT (Virtual File Allocation Table), Git и Cassandra (база данных NoSQL). Так что именно определяет такое понятие, как «файловая система»?
Основы файловой системы
Ядро Linux имеет определенные требования к сущности, которая может считаться файловой системой. Она должна реализовывать методы open() , read() и write() для постоянных объектов, которые имеют имена. С точки зрения объектно-ориентированного программирования, ядро определяет обобщенную файловую систему (generic filesystem) в качестве абстрактного интерфейса, а эти три большие функции считаются «виртуальными» и не имеют конкретного определения. Соответственно, реализация файловой системы по умолчанию называется виртуальной файловой системой (VFS).

Если мы можем открывать, читать и записывать в сущность, то эта сущность считается файлом, как мы видим из примера в консоли сверху.
Феномен VFS лишь подчеркивает наблюдение, характерное для Unix-подобных систем, которое гласит, что «всё является файлом». Подумайте, насколько странно, что тот маленький пример сверху с /dev/console показывает, как на самом деле работает консоль. На картинке изображена интерактивная Bash сессия. Отправка строки в консоль (virtual console device) отображает ее на виртуальном экране. VFS имеет другие, еще более странные свойства. Например, она дает возможность осуществлять поиск по ним.
Знакомые нам системы, такие как ext4, NFS и /proc имеют три важные функции в структуре данных С, которая называется file_operations. Кроме того, определенные файловые системы расширяют и переопределяют функции VFS привычным объектно-ориентированным способом. Как отмечает Роберт Лав, абстракция VFS позволяет пользователям Linux беспечно копировать файлы в или из сторонних операционных систем или абстрактных сущностей, таких как pipes, не беспокоясь об их внутреннем формате данных. Со стороны пользователя (userspace) с помощью системного вызова процесс может копировать из файла в структуры данных ядра с помощью метода read() одной файловой системы, а затем использовать метод write() другой файловой системы для вывода данных.
Определения функций, которые принадлежат к базовым типам VFS, находятся в файлах fs/*.c исходного кода ядра, в то время как подкаталоги fs/ содержат определенные файловые системы. В ядре также содержатся сущности, такие как cgroups , /dev и tmpfs , которые требуются в процессе загрузки и поэтому определяются в подкаталоге ядра init/ . Заметьте, что cgroups , /dev и tmpfs не вызывают «большую тройку» функций file_operations , а напрямую читают и пишут в память.
На приведенной ниже диаграмме показано, как userspace обращается к различным типам файловых систем, обычно монтируемых в системах Linux. Не показаны такие конструкции как pipes , dmesg и POSIX clocks , которые также реализуют структуру file_operations , доступ к которым проходит через слой VFS.

VFS — это «слой оболочки» между системными вызовами и реализациями определенных file_operations , таких как ext4 и procfs . Функции file_operations могут взаимодействовать либо с драйверами устройств, либо с устройствами доступа к памяти. tmpfs , devtmpfs и cgroups не используют file_operations , а напрямую обращаются к памяти.
Существование VFS обеспечивает возможность переиспользовать код, так как основные методы, связанные с файловыми системами, не должны быть повторно реализованы каждым типом файловой системы. Переиспользование кода – широкоприменяемая практика программных инженеров! Однако, если повторно используемый код содержит серьезные ошибки, от них страдают все реализации, которые наследуют общие методы.
/tmp: Простая подсказка
Простой способ обнаружить, что VFS присутствуют в системе – это ввести mount | grep -v sd | grep -v :/ , что покажет все смонтированные ( mounted ) файловые системы, которые не являются резидентами на диске и не NFS, что справедливо на большинстве компьютеров. Одним из перечисленных маунтов ( mounts ) VFS, несомненно, будет /tmp , верно?

Все знают, что хранение /tmp на физическом носителе – безумие! Источник.
Почему нежелательно хранить /tmp на физическом носителе? Потому что файлы в /tmp являются временными, а устройства хранения медленнее, чем память, где создается tmpfs. Более того, физические носители более подвержены износу при перезаписи, чем память. Наконец, файлы в /tmp могут содержать конфиденциальную информацию, поэтому их исчезновение при каждой перезагрузке является неотъемлемой функцией.
К сожалению, некоторые скрипты инсталляции Linux дистрибутивов создают /tmp на устройстве хранения по умолчанию. Не отчаивайтесь, если это произошло и с вашей системой. Выполните несколько простых инструкций с Arch Wiki, чтобы это исправить, и помните о том, что память выделенная для tmpfs , становится недоступной для других целей. Другими словами, система с гигантской tmpfs и большими файлами в ней может израсходовать всю память и упасть. Другая подсказка: во время редактирования файла /etc/fstab , помните о том, что он должен заканчиваться новой строкой, иначе ваша система не загрузится.
Помимо /tmp , VFS (виртуальные файловые системы), которые наиболее знакомы пользователям Linux – это /proc и /sys . ( /dev располагается в общей памяти и не имеет file_operations ). Почему именно эти два компонента? Давайте разберемся в этом вопросе.
procfs создает снимок мгновенного состояния ядра и процессов, которые он контролирует для userspace . В /proc ядро выводит информацию о том, какими средствами оно располагает, например, прерывания, виртуальная память и планировщик. Кроме того, /proc/sys – это место, где параметры, настраиваемые с помощью команды sysctl , доступны для userspace . Статус и статистика отдельных процессов выводится в каталогах /proc/ .

Здесь /proc/meminfo — это пустой файл, который тем не менее содержит ценную информацию.
Поведение /proc файлов показывает, какими непохожими могут быть дисковые файловые системы VFS. С одной стороны, /proc/meminfo содержат информацию, которую можно посмотреть командой free . С другой же, там пусто! Как так получается? Ситуация напоминает знаменитую статью под названием «Существует ли луна, когда на нее никто не смотрит? Реальность и квантовая теория», написанную профессором физики Корнельского университета Дэвидом Мермином в 1985 году. Дело в том, что ядро собирает статистику памяти, когда происходит запрос к /proc , и на самом деле в файлах /proc ничего нет, когда никто туда не смотрит. Как сказал Мермин, «Фундаментальная квантовая доктрина гласит, что измерение, как правило, не выявляет ранее существовавшего значения измеряемого свойства.» (А над вопросом про луну подумайте в качестве домашнего задания!)
Кажущаяся пустота procfs имеет смысл, поскольку располагающаяся там информация динамична. Немного другая ситуация с sysfs . Давайте сравним, сколько файлов размером не менее одного байта есть в /proc и в /sys .

Procfs имеет один файл, а именно экспортированную конфигурацию ядра, которая является исключением, поскольку ее нужно генерировать только один раз за загрузку. С другой стороны, в /sys лежит множество более объемных файлов, многие из которых занимают целую страницу памяти. Обычно файлы sysfs содержат ровно одно число или строку, в отличие от таблиц информации, получаемой при чтении таких файлов, как /proc/meminfo .
Цель sysfs – предоставить свойства доступные для чтения и записи того, что ядро называет «kobjects» в userspace. Единственная цель kobjects – это подсчет ссылок: когда удаляется последняя ссылка на kobject, система восстановит ресурсы, связанные с ним. Тем не менее, /sys составляет большую часть знаменитого «stable ABI для userspace» ядра, которое никто никогда, ни при каких обстоятельствах не может «сломать». Это не означает, что файлы в sysfs статичны, что противоречило бы подсчету ссылок на нестабильные объекты.
Стабильный двоичный интерфейс приложений ядра (kernel’s stable ABI) ограничивает то, что может появиться в /sys , а не то, что на самом деле присутствует в данный конкретный момент. Листинг разрешений на файлы в sysfs обеспечивает понимание того, как конфигурируемые параметры устройств, модулей, файловых систем и т.д. могут быть настроены или прочитаны. Делаем логический вывод, что procfs также является частью stable ABI ядра, хотя это не указано явно в документации.

Файлы в sysfs описывают одно конкретное свойство для каждой сущности и могут быть читаемыми, перезаписываемыми или и то и другое сразу. «0» в файле говорит о том, что SSD не может быть удален.
Вторую часть перевода начнем с того, как наблюдать за VFS с помощью инструментов eBPF и bcc, а сейчас ждем ваши комментарии и традиционно приглашаем на открытый вебинар, который уже 9 апреля проведет наш преподаватель — Владимир Дроздецкий.