Android 12 obsługuje przekazywanie FUSE, co minimalizuje obciążenie FUSE, aby osiągnąć wydajność porównywalną z bezpośrednim dostępem do niższego systemu plików. Przekazywanie FUSE jest obsługiwane w jądrach android12-5.4
, android12-5.10
i android-mainline
(tylko testowe), co oznacza, że obsługa tej funkcji zależy od jądra używanego przez urządzenie i wersji Androida, na której działa urządzenie:
Urządzenia uaktualniające się z Androida 11 do Androida 12 nie obsługują przechodzenia FUSE, ponieważ jądra tych urządzeń są zamrożone i nie można przejść do jądra, które zostało oficjalnie zaktualizowane poprzez zmiany w przechodzeniu FUSE.
Urządzenia uruchamiane z systemem Android 12 mogą obsługiwać przekazywanie FUSE podczas korzystania z oficjalnego jądra. W przypadku takich urządzeń kod platformy Android implementujący przekazywanie FUSE jest osadzony w głównym module MediaProvider , który jest automatycznie aktualizowany. Urządzenia, które nie implementują MediaProvider jako modułu głównego (na przykład urządzenia z systemem Android Go), mogą również uzyskać dostęp do zmian MediaProvider, gdy są one udostępniane publicznie.
FUSE kontra SDCardFS
System plików w przestrzeni użytkownika (FUSE) to mechanizm, który umożliwia zlecanie operacji wykonywanych w systemie plików FUSE przez jądro (sterownik FUSE) programowi przestrzeni użytkownika (demon FUSE), który implementuje te operacje. Android 11 wycofał SDCardFS i uczynił FUSE domyślnym rozwiązaniem do emulacji pamięci masowej. W ramach tej zmiany system Android zaimplementował własnego demona FUSE, który przechwytuje dostęp do plików, wymusza dodatkowe funkcje bezpieczeństwa i prywatności oraz manipuluje plikami w czasie wykonywania.
Chociaż FUSE radzi sobie dobrze z informacjami buforowanymi, takimi jak strony lub atrybuty, wprowadza regresję wydajności podczas uzyskiwania dostępu do pamięci zewnętrznej, co jest szczególnie widoczne na urządzeniach ze średniej i niższej półki. Regresje te spowodowane są łańcuchem komponentów współpracujących przy implementacji systemu plików FUSE, a także wielokrotnymi przełączeniami z przestrzeni jądra do przestrzeni użytkownika w komunikacji pomiędzy sterownikiem FUSE a demonem FUSE (w porównaniu z bezpośrednim dostępem do niższego pliku system, który jest oszczędniejszy i całkowicie zaimplementowany w jądrze).
Aby złagodzić te regresje, aplikacje mogą używać splicingu w celu ograniczenia kopiowania danych i korzystać z interfejsu API ContentProvider , aby uzyskać bezpośredni dostęp do niższych plików systemu plików. Nawet przy tych i innych optymalizacjach operacje odczytu i zapisu mogą powodować zmniejszoną przepustowość podczas korzystania z FUSE w porównaniu z bezpośrednim dostępem do niższego systemu plików - szczególnie w przypadku operacji odczytu losowego, gdzie nie może pomóc żadne buforowanie ani odczyt z wyprzedzeniem. W przypadku aplikacji uzyskujących bezpośredni dostęp do pamięci masowej za pośrednictwem starszej ścieżki /sdcard/
w dalszym ciągu zauważalne są spadki wydajności, zwłaszcza podczas wykonywania operacji wymagających dużej liczby operacji we/wy.
Żądania przestrzeni użytkownika SDcardFS
Korzystanie z SDcardFS może przyspieszyć emulację pamięci masowej i sprawdzanie uprawnień FUSE poprzez usunięcie wywołania przestrzeni użytkownika z jądra. Żądania dotyczące przestrzeni użytkownika podążają ścieżką: Przestrzeń użytkownika → VFS → sdcardfs → VFS → ext4 → Pamięć podręczna strony/Pamięć.
Rysunek 1. Żądania przestrzeni użytkownika SDcardFS
Żądania dotyczące przestrzeni użytkownika FUSE
Początkowo FUSE był używany do umożliwienia emulacji pamięci masowej i umożliwienia aplikacjom przejrzystego korzystania z pamięci wewnętrznej lub zewnętrznej karty SD. Korzystanie z FUSE wiąże się z pewnym obciążeniem, ponieważ każde żądanie przestrzeni użytkownika podąża ścieżką: Przestrzeń użytkownika → VFS → Sterownik FUSE → Demon FUSE → VFS → ext4 → Pamięć podręczna/pamięć stron.
Rysunek 2. Żądania przestrzeni użytkownika FUSE
Żądania przejścia FUSE
Większość uprawnień dostępu do plików jest sprawdzana podczas otwierania pliku, a dodatkowe kontrole uprawnień mają miejsce podczas odczytu i zapisu do pliku. W niektórych przypadkach można już w momencie otwarcia pliku wiedzieć, że aplikacja żądająca ma pełny dostęp do żądanego pliku, więc system nie musi w dalszym ciągu przekazywać żądań odczytu i zapisu ze sterownika FUSE do demona FUSE (ponieważ przeniesie jedynie dane z jednego miejsca do drugiego).
Dzięki przejściu FUSE demon FUSE obsługujący otwarte żądanie może powiadomić sterownik FUSE, że operacja jest dozwolona i że wszystkie kolejne żądania odczytu i zapisu mogą być bezpośrednio przekazywane do niższego systemu plików. Pozwala to uniknąć dodatkowego narzutu związanego z oczekiwaniem, aż demon FUSE przestrzeni użytkownika odpowie na żądania sterownika FUSE.
Poniżej pokazano porównanie żądań przejścia FUSE i FUSE.
Rysunek 3. Żądanie FUSE a żądanie przejścia FUSE
Gdy aplikacja uzyskuje dostęp do systemu plików FUSE, wykonywane są następujące operacje:
Sterownik FUSE obsługuje i kolejkuje żądanie, a następnie przedstawia je demonowi FUSE, który obsługuje ten system plików FUSE poprzez określoną instancję połączenia w pliku
/dev/fuse
, którego odczytanie przez demona FUSE jest zablokowane.Kiedy demon FUSE otrzymuje żądanie otwarcia pliku, decyduje, czy dla tego konkretnego pliku powinno być dostępne przejście FUSE. Jeśli jest dostępny, demon:
Powiadamia sterownik FUSE o tym żądaniu.
Włącza przekazywanie FUSE dla pliku przy użyciu ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, które należy wykonać na deskryptorze pliku otwartego/dev/fuse
.
Ioctl otrzymuje (jako parametr) strukturę danych zawierającą następujące elementy:
Deskryptor pliku niższego pliku systemu plików, który jest celem funkcji przekazywania.
Unikalny identyfikator żądania FUSE, które jest aktualnie obsługiwane (musi być otwarte lub utwórz i otwórz).
Dodatkowe pola, które można pozostawić puste i są przeznaczone do przyszłych wdrożeń.
Jeśli ioctl powiedzie się, demon FUSE kończy żądanie otwarcia, sterownik FUSE obsługuje odpowiedź demona FUSE, a do pliku FUSE w jądrze dodawane jest odniesienie do niższego pliku systemu plików. Gdy aplikacja żąda operacji odczytu/zapisu na pliku FUSE, sterownik FUSE sprawdza, czy dostępne jest odniesienie do pliku niższego poziomu systemu plików.
Jeśli dostępne jest odniesienie, sterownik tworzy nowe żądanie wirtualnego systemu plików (VFS) z tymi samymi parametrami, ukierunkowane na niższy plik systemu plików.
Jeśli odniesienie nie jest dostępne, sterownik przekazuje żądanie do demona FUSE.
Powyższe operacje dotyczą odczytu/zapisu i odczytu/zapisu-itera na plikach ogólnych oraz operacji odczytu/zapisu na plikach mapowanych w pamięci. Przekazywanie FUSE dla danego pliku istnieje do momentu zamknięcia tego pliku.
Zaimplementuj przekazywanie FUSE
Aby włączyć przekazywanie FUSE na urządzeniach z systemem Android 12, dodaj następujące wiersze do pliku $ANDROID_BUILD_TOP/device/…/device.mk
urządzenia docelowego.
# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
persist.sys.fuse.passthrough.enable=true
Aby wyłączyć przekazywanie FUSE, pomiń powyższą zmianę konfiguracji lub ustaw persist.sys.fuse.passthrough.enable
na false
. Jeśli wcześniej włączyłeś przekazywanie FUSE, wyłączenie go uniemożliwia urządzeniu korzystanie z przechodzenia FUSE, ale urządzenie nadal działa.
Aby włączyć/wyłączyć przekazywanie FUSE bez flashowania urządzenia, zmień właściwości systemu za pomocą poleceń ADB. Przykład jest pokazany poniżej.
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Aby uzyskać dodatkową pomoc, zapoznaj się z implementacją referencyjną .
Sprawdź poprawność przejścia FUSE
Aby sprawdzić, czy MediaProvider korzysta z przekazywania FUSE, sprawdź logcat
pod kątem komunikatów debugowania. Na przykład:
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
w dzienniku gwarantuje, że przejście FUSE jest używane.
System CTS systemu Android 12 zawiera CtsStorageTest
, który zawiera testy wyzwalające przekazywanie FUSE. Aby uruchomić test ręcznie, użyj atestu, jak pokazano poniżej:
atest CtsStorageTest