Android 12 unterstützt FUSE-Passthrough, wodurch der FUSE-Overhead minimiert wird, um eine Leistung zu erzielen, die mit dem direkten Zugriff auf das untergeordnete Dateisystem vergleichbar ist. FUSE-Passthrough wird in den Kerneln android12-5.4
, android12-5.10
und android-mainline
(nur für Tests) unterstützt. Das bedeutet, dass die Unterstützung dieser Funktion vom vom Gerät verwendeten Kernel und der auf dem Gerät ausgeführten Android-Version abhängt:
Geräte, die von Android 11 auf Android 12 umgestellt werden, können FUSE-Passthrough nicht unterstützen, da die Kernel dieser Geräte eingefroren sind und nicht auf einen Kernel umgestellt werden können, der offiziell mit den FUSE-Passthrough-Änderungen aktualisiert wurde.
Geräte, die mit Android 12 ausgeliefert werden, können FUSE-Passthrough unterstützen, wenn ein offizieller Kernel verwendet wird. Auf solchen Geräten ist der Android-Framework-Code, der die FUSE-Passthrough-Funktion implementiert, in das Hauptmodul MediaProvider eingebettet, das automatisch aktualisiert wird. Geräte, auf denen MediaProvider nicht als Mainstream-Modul implementiert ist (z. B. Android Go-Geräte), können ebenfalls auf MediaProvider-Änderungen zugreifen, da sie öffentlich freigegeben werden.
FUSE im Vergleich zu SDCardFS
Filesystem in Userspace (FUSE) ist ein Mechanismus, mit dem Vorgänge, die in einem FUSE-Dateisystem ausgeführt werden, vom Kernel (FUSE-Treiber) an ein Userspace-Programm (FUSE-Daemon) ausgelagert werden können, das die Vorgänge implementiert. In Android 11 wurde SDCardFS eingestellt und FUSE zur Standardlösung für die Speicheremulation gemacht. Im Rahmen dieser Änderung hat Android seinen eigenen FUSE-Daemon implementiert, um Dateizugriffe abzufangen, zusätzliche Sicherheits- und Datenschutzfunktionen durchzusetzen und Dateien zur Laufzeit zu manipulieren.
FUSE bietet zwar eine gute Leistung bei der Verarbeitung von Informationen, die im Cache gespeichert werden können, wie Seiten oder Attribute, führt aber zu Leistungseinbußen beim Zugriff auf externen Speicher, die sich besonders auf Mittel- und Einsteigergeräten bemerkbar machen. Diese Rückschritte werden durch eine Kette von Komponenten verursacht, die bei der Implementierung des FUSE-Dateisystems zusammenarbeiten, sowie durch mehrere Wechsel vom Kernel- zum Userspace bei der Kommunikation zwischen dem FUSE-Treiber und dem FUSE-Daemon (im Vergleich zum direkten Zugriff auf das untergeordnete Dateisystem, das schlanker und vollständig im Kernel implementiert ist).
Um diese Rückschritte zu vermeiden, können Apps Splicing verwenden, um das Kopieren von Daten zu reduzieren, und die ContentProvider API, um direkten Zugriff auf Dateien des untergeordneten Dateisystems zu erhalten. Selbst bei diesen und anderen Optimierungen kann die Bandbreite bei Lese- und Schreibvorgängen bei Verwendung von FUSE im Vergleich zum direkten Zugriff auf das untergeordnete Dateisystem reduziert sein. –  insbesondere bei zufälligen Lesevorgängen, bei denen kein Caching oder Vorablesen helfen kann. Bei Apps, die über den alten Pfad /sdcard/
direkt auf den Speicher zugreifen, treten weiterhin spürbare Leistungseinbußen auf, insbesondere bei speicherintensiven Vorgängen.
SDcardFS-Nutzerraumanfragen
Mit SDcardFS können die Speicheremulation und Berechtigungsprüfungen von FUSE beschleunigt werden, da der Aufruf des Nutzerbereichs aus dem Kernel entfernt wird. Userspace-Anfragen folgen dem Pfad: Userspace → VFS → sdcardfs → VFS → ext4 → Auslagerung/Speicher.
Abbildung 1: SDcardFS-Nutzerraumanfragen
FUSE-Nutzerbereichsanfragen
Ursprünglich wurde FUSE verwendet, um die Speicheremulation zu ermöglichen und Apps die transparente Nutzung des internen Speichers oder einer externen SD-Karte zu ermöglichen. Die Verwendung von FUSE führt zu einem gewissen Overhead, da jede Userspace-Anfrage den Pfad Userspace → VFS → FUSE-Treiber → FUSE-Daemon → VFS → ext4 → Auslagerung/Speicher durchläuft.
Abbildung 2: FUSE-Nutzerbereichsanfragen
FUSE-Weiterleitungsanfragen
Die meisten Dateizugriffsberechtigungen werden beim Öffnen der Datei geprüft. Zusätzliche Berechtigungsprüfungen erfolgen beim Lesen von und Schreiben in diese Datei. In einigen Fällen ist es beim Öffnen der Datei möglich, zu erkennen, dass die anfragende App vollen Zugriff auf die angeforderte Datei hat. Das System muss dann die Lese- und Schreibanfragen nicht weiter vom FUSE-Treiber an den FUSE-Daemon weiterleiten, da dadurch nur Daten von einem Ort an einen anderen verschoben würden.
Bei der FUSE-Weiterleitung kann der FUSE-Daemon, der eine offene Anfrage verarbeitet, den FUSE-Treiber darüber informieren, dass der Vorgang zulässig ist und dass alle nachfolgenden Lese- und Schreibanfragen direkt an das untergeordnete Dateisystem weitergeleitet werden können. So wird der zusätzliche Overhead vermieden, der durch das Warten auf die Antwort des FUSE-Daemons im Userspace auf die FUSE-Treiberanfragen entsteht.
Unten ist ein Vergleich von FUSE- und FUSE-Passthrough-Anfragen zu sehen.
Abbildung 3: FUSE-Anfrage und FUSE-Weiterleitungsanfrage
Wenn eine App auf das FUSE-Dateisystem zugreift, werden die folgenden Vorgänge ausgeführt:
Der FUSE-Treiber verarbeitet und stellt die Anfrage in die Warteschlange, bevor er sie an den FUSE-Daemon weiterleitet, der das FUSE-Dateisystem über eine bestimmte Verbindungsinstanz in der Datei
/dev/fuse
verwaltet. Der FUSE-Daemon ist jedoch vom Lesen der Datei ausgeschlossen.Wenn der FUSE-Daemon eine Anfrage zum Öffnen einer Datei erhält, entscheidet er, ob die FUSE-Weiterleitung für diese Datei verfügbar sein soll. Wenn er verfügbar ist, führt der Daemon folgende Aktionen aus:
Der FUSE-Treiber wird über diese Anfrage benachrichtigt.
Aktiviert die FUSE-Durchleitung für die Datei mit dem
FUSE_DEV_IOC_PASSTHROUGH_OPEN
-ioctl, das auf dem Dateideskriptor der geöffneten/dev/fuse
ausgeführt werden muss.
ioctl empfängt (als Parameter) eine Datenstruktur, die Folgendes enthält:
Dateideskriptor der Datei des untergeordneten Dateisystems, die das Ziel der Passthrough-Funktion ist.
Eindeutige Kennung der FUSE-Anfrage, die derzeit verarbeitet wird (muss „open“ oder „create-and-open“ sein).
Zusätzliche Felder, die leer bleiben können und für zukünftige Implementierungen gedacht sind.
Wenn der ioctl erfolgreich ist, führt der FUSE-Daemon die Öffnungsanfrage aus, der FUSE-Treiber verarbeitet die Antwort des FUSE-Daemons und der FUSE-Datei wird im Kernel ein Verweis auf die untergeordnete Dateisystemdatei hinzugefügt. Wenn eine App einen Lese-/Schreibvorgang für eine FUSE-Datei anfordert, prüft der FUSE-Treiber, ob der Verweis auf eine untergeordnete Datei des Dateisystems verfügbar ist.
Wenn eine Referenz verfügbar ist, erstellt der Treiber eine neue VFS-Anfrage (Virtual File System) mit denselben Parametern, die auf die untergeordnete Dateisystemdatei ausgerichtet ist.
Wenn keine Referenz verfügbar ist, leitet der Treiber die Anfrage an den FUSE-Daemon weiter.
Die oben genannten Vorgänge treten bei Lese-/Schreib- und Lese-/Schreib-Iterationen auf generischen Dateien und Lese-/Schreibvorgängen auf speicherabgebildeten Dateien auf. Der FUSE-Passthrough für eine bestimmte Datei ist verfügbar, bis diese Datei geschlossen wird.
FUSE-Passthrough implementieren
Wenn Sie den FUSE-Passthrough auf Geräten mit Android 12 aktivieren möchten, fügen Sie der Datei $ANDROID_BUILD_TOP/device/…/device.mk
des Zielgeräts die folgenden Zeilen hinzu.
# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
persist.sys.fuse.passthrough.enable=true
Wenn Sie die FUSE-Passthrough-Funktion deaktivieren möchten, lassen Sie die oben genannte Konfigurationsänderung aus oder setzen Sie persist.sys.fuse.passthrough.enable
auf false
. Wenn Sie FUSE Passthrough zuvor aktiviert haben, wird das Gerät dadurch daran gehindert, FUSE Passthrough zu verwenden. Das Gerät bleibt jedoch funktionsfähig.
Wenn Sie die FUSE-Passthrough-Funktion aktivieren oder deaktivieren möchten, ohne das Gerät zu flashen, ändern Sie die Systemeigenschaft mit ADB-Befehlen. Hier ein Beispiel:
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Weitere Informationen finden Sie in der Referenzimplementierung.
FUSE-Passthrough prüfen
Prüfe in logcat
, ob MediaProvider FUSE-Passthrough verwendet. Beispiel:
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...
Der Eintrag FuseDaemon: Using FUSE passthrough
im Protokoll gibt an, dass die FUSE-Passthrough-Funktion verwendet wird.
Die Android 12 CTS enthält CtsStorageTest
, einschließlich Tests, die FUSE-Passthrough auslösen. Wenn Sie den Test manuell ausführen möchten, verwenden Sie den Befehl „atest“, wie unten dargestellt:
atest CtsStorageTest