проходной FUSE

Android 12 поддерживает сквозную передачу FUSE, которая минимизирует накладные расходы FUSE, обеспечивая производительность, сравнимую с прямым доступом к файловой системе более низкого уровня. Сквозная передача FUSE поддерживается в ядрах android12-5.4 , android12-5.10 и android-mainline (только для тестирования), что означает, что поддержка этой функции зависит от используемого на устройстве ядра и версии Android, установленной на устройстве.

  • Устройства, обновляющиеся с Android 11 до Android 12, не поддерживают сквозную передачу FUSE, поскольку ядра для этих устройств заморожены, и они не могут перейти на ядро, официально обновленное с учетом изменений, связанных со сквозной передачей FUSE.

  • Устройства, работающие под управлением Android 12, могут поддерживать сквозную передачу FUSE при использовании официального ядра. Для таких устройств код фреймворка Android, реализующий сквозную передачу FUSE, встроен в основной модуль MediaProvider , который автоматически обновляется. Устройства, которые не реализуют MediaProvider в качестве основного модуля (например, устройства Android Go), также могут получить доступ к изменениям MediaProvider, поскольку они являются общедоступными.

FUSE против SDcardFS

Файловая система в пользовательском пространстве (FUSE) — это механизм, позволяющий ядру (драйверу FUSE) передавать операции, выполняемые в файловой системе FUSE, программе пользовательского пространства (демону FUSE), которая реализует эти операции. В Android 11 была отменена SDCardFS , и FUSE стала решением по умолчанию для эмуляции хранилища. В рамках этого изменения Android реализовал собственный демон FUSE для перехвата обращений к файлам, обеспечения дополнительных функций безопасности и конфиденциальности, а также для манипулирования файлами во время выполнения.

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

Для смягчения этих проблем приложения могут использовать слияние данных (splicing) для уменьшения копирования данных и API ContentProvider для прямого доступа к файлам нижней файловой системы. Даже с учетом этих и других оптимизаций , операции чтения и записи могут демонстрировать снижение пропускной способности при использовании FUSE по сравнению с прямым доступом к файлам нижней файловой системы — особенно при операциях произвольного чтения, где кэширование или предварительное чтение не помогут. А приложения, которые напрямую обращаются к хранилищу через устаревший путь /sdcard/ продолжают испытывать заметное снижение производительности, особенно при выполнении операций с интенсивным вводом-выводом.

запросы пользовательского пространства SDcardFS

Использование SDcardFS может ускорить эмуляцию хранилища и проверку прав доступа в FUSE за счет исключения вызова из пользовательского пространства в ядре. Запросы из пользовательского пространства следуют по следующему пути: Пользовательское пространство → VFS → sdcardfs → VFS → ext4 → Кэш страниц/Хранилище.

FUSE Passthrough SDcardFS

Рисунок 1. Запросы пользовательского пространства SDcardFS.

Запросы пользовательского пространства FUSE

FUSE изначально использовался для эмуляции хранилища и для обеспечения возможности приложениям прозрачно использовать либо внутреннюю память, либо внешнюю SD-карту. Использование FUSE вносит некоторые накладные расходы, поскольку каждый запрос в пользовательском пространстве следует по следующему пути: Пользовательское пространство → VFS → драйвер FUSE → демон FUSE → VFS → ext4 → кэш страниц/хранилище.

Предохранитель, проходной предохранитель

Рисунок 2. Запросы пользовательского пространства FUSE.

Запросы на сквозную передачу FUSE

В большинстве случаев права доступа к файлам проверяются во время их открытия, а дополнительные проверки прав доступа происходят при чтении и записи в этот файл. В некоторых случаях можно уже во время открытия файла определить, что запрашивающее приложение имеет полный доступ к запрашиваемому файлу, поэтому системе не нужно продолжать пересылать запросы на чтение и запись от драйвера FUSE демону FUSE (поскольку это привело бы только к перемещению данных из одного места в другое).

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

Ниже приведено сравнение запросов FUSE и запросов FUSE passthrough.

Сравнение сквозных каналов FUSE

Рисунок 3. Запрос FUSE против запроса на сквозную передачу FUSE.

При обращении приложения к файловой системе FUSE происходят следующие операции:

  1. Драйвер FUSE обрабатывает запрос и ставит его в очередь, затем передает его демону FUSE, который управляет данной файловой системой FUSE, через определенный экземпляр соединения с файлом /dev/fuse , чтение которого демону FUSE блокируется.

  2. Когда демон FUSE получает запрос на открытие файла, он решает, должна ли быть доступна сквозная передача FUSE для этого конкретного файла. Если она доступна, демон:

    1. Уведомляет драйвер FUSE об этом запросе.

    2. Включает сквозную передачу FUSE для файла с помощью ioctl-функции FUSE_DEV_IOC_PASSTHROUGH_OPEN , которая должна быть выполнена для файлового дескриптора открытого /dev/fuse .

  3. Функция ioctl принимает (в качестве параметра) структуру данных, содержащую следующее:

    • Дескриптор файла из файловой системы более низкого уровня, являющегося целевым объектом для функции сквозной передачи.

    • Уникальный идентификатор запроса FUSE, который в данный момент обрабатывается (должен быть открыт или создан и открыт).

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

  4. Если команда ioctl выполняется успешно, демон FUSE завершает запрос на открытие файла, драйвер FUSE обрабатывает ответ демона FUSE, и в файл FUSE в ядре добавляется ссылка на файл файловой системы более низкого уровня. Когда приложение запрашивает операцию чтения/записи в файл FUSE, драйвер FUSE проверяет, доступна ли ссылка на файл файловой системы более низкого уровня.

    • Если имеется ссылка, драйвер создает новый запрос к виртуальной файловой системе (VFS) с теми же параметрами, нацеленный на файл в файловой системе более низкого уровня.

    • Если ссылка недоступна, драйвер перенаправляет запрос демону FUSE.

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

Реализуйте сквозную передачу FUSE.

Чтобы включить сквозную передачу FUSE на устройствах под управлением Android 12, добавьте следующие строки в файл $ANDROID_BUILD_TOP/device/…/device.mk целевого устройства.

# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
    persist.sys.fuse.passthrough.enable=true

Чтобы отключить сквозную передачу FUSE, пропустите указанное выше изменение конфигурации или установите параметр persist.sys.fuse.passthrough.enable в значение false . Если вы ранее включили сквозную передачу FUSE, ее отключение предотвратит использование FUSE на устройстве, но устройство останется работоспособным.

Чтобы включить/отключить сквозную передачу FUSE без прошивки устройства, измените системные свойства с помощью команд ADB. Пример показан ниже.

adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot

Для получения дополнительной помощи обратитесь к эталонной реализации .

Проверьте сквозную передачу предохранителя (FUSE passthrough).

Чтобы убедиться, что MediaProvider использует сквозную передачу FUSE, проверьте logcat на наличие отладочных сообщений. Например:

adb logcat FuseDaemon:V \*:S
--------- beginning of main
03-02 12:09:57.833  3499  3773 I FuseDaemon: Using FUSE passthrough
03-02 12:09:57.833  3499  3773 I FuseDaemon: Starting fuse...

Запись в журнале FuseDaemon: Using FUSE passthrough гарантирует, что функция FUSE passthrough используется.

В состав Android 12 CTS входит CtsStorageTest , содержащий тесты, запускающие сквозную передачу FUSE. Для запуска теста вручную используйте команду `test`, как показано ниже:

atest CtsStorageTest