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 (по сравнению с прямым доступом к файловой системе начального уровня, которая более компактна и полностью реализована в ядре).
Чтобы смягчить эти регрессии, приложения могут использовать сплайсинг для сокращения объёма копирования данных и использовать API ContentProvider для прямого доступа к файлам в файловой системе нижнего уровня. Даже с учётом этих и других оптимизаций , операции чтения и записи могут столкнуться с уменьшением пропускной способности при использовании FUSE по сравнению с прямым доступом к файловой системе нижнего уровня, особенно при случайных операциях чтения, где кэширование или опережающее чтение неэффективны. Приложения, которые напрямую обращаются к хранилищу через устаревший путь /sdcard/
, продолжают испытывать заметное падение производительности, особенно при выполнении операций с интенсивным вводом-выводом.
Запросы пользовательского пространства SDcardFS
Использование SDcardFS может ускорить эмуляцию хранилища и проверку разрешений FUSE, убрав вызов пользовательского пространства из ядра. Запросы к пользовательскому пространству следуют по пути: Пользовательское пространство → VFS → sdcardfs → VFS → ext4 → Кэш страниц/Хранилище.
Рисунок 1. Запросы пользовательского пространства SDcardFS
Запросы пользовательского пространства FUSE
Изначально FUSE использовался для эмуляции хранилища и позволял приложениям прозрачно использовать как внутреннюю память, так и внешнюю SD-карту. Использование FUSE приводит к некоторым накладным расходам, поскольку каждый запрос к пользовательскому пространству следует по следующему пути: пользовательское пространство → VFS → драйвер FUSE → демон FUSE → VFS → ext4 → кэш страниц/хранилище.
Рисунок 2. Запросы пользовательского пространства FUSE
Запросы FUSE на сквозную передачу
Большинство разрешений на доступ к файлу проверяются при его открытии, а дополнительные проверки разрешений производятся при чтении и записи в этот файл. В некоторых случаях при открытии файла можно определить, что запрашивающее приложение имеет полный доступ к запрашиваемому файлу, поэтому системе не нужно продолжать пересылать запросы на чтение и запись от драйвера FUSE демону FUSE (поскольку это привело бы лишь к перемещению данных из одного места в другое).
Благодаря сквозной передаче данных FUSE демон FUSE, обрабатывающий запрос на открытие, может уведомить драйвер FUSE о том, что операция разрешена, и что все последующие запросы на чтение и запись могут быть напрямую перенаправлены в файловую систему нижнего уровня. Это позволяет избежать дополнительных накладных расходов, связанных с ожиданием ответа демона FUSE пользовательского пространства на запросы драйвера FUSE.
Сравнение запросов FUSE и сквозного пропуска FUSE показано ниже.
Рисунок 3. Запрос FUSE и запрос FUSE-передачи
Когда приложение осуществляет доступ к файловой системе FUSE, происходят следующие операции:
Драйвер FUSE обрабатывает запрос и ставит его в очередь, а затем передает его демону FUSE, который обрабатывает файловую систему FUSE через определенный экземпляр соединения в файле
/dev/fuse
, чтение которого демону FUSE запрещено.Когда демон FUSE получает запрос на открытие файла, он решает, следует ли разрешить сквозное подключение FUSE для этого файла. Если оно разрешено, демон:
Уведомляет драйвер FUSE об этом запросе.
Включает сквозную передачу FUSE для файла с помощью ioctl-вызова
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, который должен быть выполнен для файлового дескриптора открытого/dev/fuse
.
Ioctl получает (в качестве параметра) структуру данных, содержащую следующее:
Дескриптор файла нижней файловой системы, который является целью функции сквозной передачи.
Уникальный идентификатор запроса FUSE, который в данный момент обрабатывается (должен быть открытым или созданным и открытым).
Дополнительные поля, которые можно оставить пустыми и которые предназначены для будущих реализаций.
Если 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
Чтобы убедиться, что 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. Чтобы запустить тест вручную, используйте atest, как показано ниже:
atest CtsStorageTest