ПРОХОДНОЙ ПРЕДОХРАНИТЕЛЬ

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 за счет удаления вызова пользовательского пространства из ядра. Запросы пользовательского пространства следуют по пути: Userspace → VFS → sdcardfs → VFS → ext4 → Кэш/хранилище страниц.

FUSE Passthrough SDcardFS

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

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

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

FUSE Проходной FUSE

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

Транзитные запросы FUSE {#fuse-passthrough-requests}

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

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

Ниже показано сравнение сквозных запросов FUSE и FUSE.

Сравнение FUSE Passthrough

Рис. 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

Чтобы убедиться, что 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.

Android 12 CTS включает CtsStorageTest , который включает тесты, запускающие сквозную передачу FUSE. Чтобы запустить тест вручную, используйте atest, как показано ниже:

atest CtsStorageTest