проходной FUSE

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

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

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

FUSE против SDCardFS

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

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

Чтобы смягчить эти регрессии, приложения могут использовать сплайсинг для уменьшения копирования данных и использовать 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 passthrough демон FUSE, обрабатывающий открытый запрос, может уведомить драйвер FUSE о том, что операция разрешена и что все последующие запросы на чтение и запись могут быть напрямую перенаправлены в файловую систему нижнего уровня. Это позволяет избежать дополнительных накладных расходов на ожидание ответа демона FUSE пользовательского пространства на запросы драйвера FUSE.

Сравнение запросов FUSE и FUSE passthrough показано ниже.

Сравнение пропускной способности FUSE

Рисунок 3. Запрос FUSE и запрос FUSE passthrough

Когда приложение осуществляет доступ к файловой системе 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 passthrough, пропустите указанное выше изменение конфигурации или установите persist.sys.fuse.passthrough.enable в false . Если вы ранее включили FUSE passthrough, его отключение не позволит устройству использовать FUSE passthrough, но устройство останется работоспособным.

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

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

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

Проверить проход FUSE

Чтобы проверить, что MediaProvider использует FUSE passthrough, проверьте 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 passthrough. Чтобы запустить тест вручную, используйте atest, как показано ниже:

atest CtsStorageTest