Android 12 obsługuje przekazywanie FUSE, co minimalizuje narzut FUSE i pozwala 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 testy), 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, które przechodzą z Androida 11 na Androida 12, nie mogą obsługiwać przekazywania FUSE, ponieważ ich jądra są zamrożone i nie można ich przenieść do jądra, które zostało oficjalnie zaktualizowane o zmiany związane z przekazywaniem FUSE.
Urządzenia z Androidem 12 mogą obsługiwać przekazywanie FUSE, gdy używają oficjalnego jądra. W przypadku takich urządzeń kod platformy Android, który implementuje 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 (np. urządzenia z Androidem Go), również mogą uzyskać dostęp do zmian w MediaProvider, gdy zostaną one publicznie udostępnione.
FUSE a SDCardFS
System plików w przestrzeni użytkownika (FUSE) to mechanizm, który umożliwia przekazywanie operacji wykonywanych w systemie plików FUSE przez jądro (sterownik FUSE) do programu w przestrzeni użytkownika (demon FUSE), który implementuje te operacje. Android 11 wycofał SDCardFS i ustawił FUSE jako domyślne rozwiązanie do emulacji pamięci. W ramach tej zmiany Android wdrożył własny demon FUSE, który przechwytuje dostęp do plików, wymusza dodatkowe funkcje zabezpieczeń i ochrony prywatności oraz manipuluje plikami w czasie działania.
FUSE dobrze radzi sobie z informacjami, które można przechowywać w pamięci podręcznej, takimi jak strony czy atrybuty, ale powoduje regresję wydajności podczas uzyskiwania dostępu do zewnętrznej pamięci masowej, co jest szczególnie widoczne na urządzeniach ze średniej i niskiej półki. Te regresje są spowodowane przez łańcuch komponentów współpracujących przy implementacji systemu plików FUSE, a także przez wielokrotne przełączanie się z przestrzeni jądra do przestrzeni użytkownika w komunikacji między sterownikiem FUSE a demonem FUSE (w porównaniu z bezpośrednim dostępem do niższego systemu plików, który jest bardziej uproszczony i w całości zaimplementowany w jądrze).
Aby złagodzić te regresje, aplikacje mogą używać łączenia, aby zmniejszyć kopiowanie danych, oraz interfejsu ContentProvider API, aby uzyskać bezpośredni dostęp do plików systemu plików niższego poziomu. Nawet przy tych i innych optymalizacjach operacje odczytu i zapisu mogą mieć mniejszą przepustowość podczas korzystania z FUSE w porównaniu z bezpośrednim dostępem do niższego systemu plików – zwłaszcza w przypadku operacji odczytu losowego, w których nie pomaga buforowanie ani odczyt z wyprzedzeniem. Aplikacje, które uzyskują bezpośredni dostęp do pamięci przez starszą ścieżkę /sdcard/
, nadal odczuwają zauważalny spadek wydajności, zwłaszcza podczas wykonywania operacji wymagających dużej liczby operacji wejścia/wyjścia.
Żądania przestrzeni użytkownika SDcardFS
Korzystanie z SDcardFS może przyspieszyć emulację pamięci i sprawdzanie uprawnień FUSE przez usunięcie wywołania przestrzeni użytkownika z jądra. Żądania przestrzeni użytkownika przechodzą ścieżkę: Przestrzeń użytkownika → VFS → sdcardfs → VFS → ext4 → Pamięć podręczna strony/Pamięć masowa.
Rysunek 1. Żądania przestrzeni użytkownika SDcardFS
Żądania przestrzeni użytkownika FUSE
FUSE był początkowo używany do emulacji pamięci i umożliwiał aplikacjom transparentne korzystanie z pamięci wewnętrznej lub zewnętrznej karty SD. Korzystanie z FUSE powoduje pewne obciążenie, ponieważ każde żądanie przestrzeni użytkownika przechodzi ścieżkę: Przestrzeń użytkownika → VFS → sterownik FUSE → demon FUSE → VFS → ext4 → pamięć podręczna strony/pamięć masowa.
Rysunek 2. Żądania przestrzeni użytkownika FUSE
Żądania przekazywane przez FUSE
Większość uprawnień dostępu do plików jest sprawdzana podczas otwierania pliku, a dodatkowe kontrole uprawnień są przeprowadzane podczas odczytywania i zapisywania danych w tym pliku. W niektórych przypadkach w momencie otwierania pliku można stwierdzić, że aplikacja wysyłająca żądanie ma pełny dostęp do żądanego pliku, więc system nie musi dalej przekazywać żądań odczytu i zapisu z sterownika FUSE do demona FUSE (ponieważ spowodowałoby to tylko przeniesienie danych z jednego miejsca do drugiego).
W przypadku przekazywania 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 obciążenia związanego z oczekiwaniem na odpowiedź demona FUSE w przestrzeni użytkownika na żądania sterownika FUSE.
Poniżej znajdziesz porównanie żądań FUSE i żądań przekazywanych FUSE.
Rysunek 3. Żądanie FUSE a żądanie przekazywane FUSE
Gdy aplikacja uzyskuje dostęp do systemu plików FUSE, wykonywane są te działania:
Sterownik FUSE obsługuje i kolejkuje żądanie, a następnie przekazuje je do demona FUSE, który obsługuje ten system plików FUSE za pomocą konkretnego wystąpienia połączenia w pliku
/dev/fuse
, którego demon FUSE nie może odczytać.Gdy demon FUSE otrzyma prośbę o otwarcie pliku, decyduje, czy dla tego konkretnego pliku ma być dostępny tryb przekazywania FUSE. Jeśli jest dostępny, demon:
Powiadamia sterownik FUSE o tym żądaniu.
Umożliwia przekazywanie FUSE dla pliku za pomocą
FUSE_DEV_IOC_PASSTHROUGH_OPEN
ioctl, które musi zostać wykonane na deskryptorze pliku otwartego/dev/fuse
.
Funkcja ioctl otrzymuje (jako parametr) strukturę danych, która zawiera te elementy:
Deskryptor pliku w systemie plików niższego poziomu, który jest celem funkcji przekazywania.
Unikalny identyfikator żądania FUSE, które jest obecnie obsługiwane (musi być otwarte lub utworzone i otwarte).
Dodatkowe pola, które można pozostawić puste i które są przeznaczone do przyszłych wdrożeń.
Jeśli ioctl się powiedzie, demon FUSE zakończy żądanie otwarcia, sterownik FUSE obsłuży odpowiedź demona FUSE, a do pliku FUSE w jądrze zostanie dodane odwołanie do pliku systemu plików niższego poziomu. Gdy aplikacja zażąda operacji odczytu lub zapisu w pliku FUSE, sterownik FUSE sprawdzi, czy dostępny jest odnośnik do pliku w systemie plików niższego poziomu.
Jeśli odwołanie jest dostępne, sterownik tworzy nowe żądanie wirtualnego systemu plików (VFS) z tymi samymi parametrami, które są kierowane do pliku w niższym systemie plików.
Jeśli odwołanie nie jest dostępne, sterownik przekazuje żądanie do demona FUSE.
Powyższe operacje występują w przypadku operacji odczytu/zapisu i odczytu-iteracji/zapisu-iteracji na plikach ogólnych oraz operacji odczytu/zapisu na plikach mapowanych w pamięci. Przekazywanie FUSE dla danego pliku jest aktywne do momentu jego zamknięcia.
Implementowanie przekazywania FUSE
Aby włączyć przekazywanie FUSE na urządzeniach z Androidem 12, dodaj te wiersze do pliku $ANDROID_BUILD_TOP/device/…/device.mk
na urządzeniu docelowym.
# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
persist.sys.fuse.passthrough.enable=true
Aby wyłączyć przekazywanie FUSE, pomiń powyższą zmianę konfiguracji lub ustaw wartość
persist.sys.fuse.passthrough.enable
na false
. Jeśli wcześniej włączono przekazywanie FUSE, wyłączenie tej funkcji uniemożliwi urządzeniu korzystanie z niej, ale urządzenie nadal będzie działać.
Aby włączyć lub wyłączyć przekazywanie FUSE bez flashowania urządzenia, zmień właściwość systemu za pomocą poleceń ADB. Przykład znajdziesz 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ą.
Weryfikacja przekazywania FUSE
Aby sprawdzić, czy MediaProvider korzysta z przekazywania FUSE, poszukaj komunikatów debugowania w logcat
. 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...
Wpis FuseDaemon: Using FUSE passthrough
w logu potwierdza, że używane jest przekazywanie FUSE.
Pakiet CTS dla Androida 12 zawiera CtsStorageTest
, który obejmuje testy wywołujące przekazywanie FUSE. Aby ręcznie uruchomić test, użyj polecenia atest w sposób pokazany poniżej:
atest CtsStorageTest