Android 12 obsługuje przekazywanie FUSE, co minimalizuje obciążenie 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 testowanie), 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 są aktualizowane z Androida 11 do Androida 12, nie mogą obsługiwać przekazywania FUSE, ponieważ jądra tych urządzeń są zamrożone i nie można ich przenieść do jądra, które zostało oficjalnie zaktualizowane o zmiany dotyczące przekazywania FUSE.
Urządzenia, które są wprowadzane na rynek z Androidem 12, mogą obsługiwać przekazywanie FUSE, jeśli używają oficjalnego jądra. W przypadku takich urządzeń kod platformy Android , który implementuje przekazywanie FUSE, jest osadzony w module głównym MediaProvider, który jest automatycznie aktualizowany. Urządzenia, które nie implementują MediaProvider jako modułu głównego (np. urządzenia z Androidem Go), mogą też korzystać ze zmian w MediaProvider, ponieważ są one publicznie udostępniane.
FUSE a SDCardFS
System plików w przestrzeni użytkownika (FUSE) to mechanizm, który umożliwia jądru (sterownikowi FUSE) przekazywanie operacji wykonywanych w systemie plików FUSE do programu w przestrzeni użytkownika (demona FUSE), który implementuje te operacje. Android 11 wycofał SDCardFS i uczynił FUSE domyślnym rozwiązaniem do emulacji pamięci. W ramach tej zmiany Android zaimplementował własnego demona FUSE, aby przechwytywać dostęp do plików, wymuszać dodatkowe funkcje bezpieczeństwa i prywatności oraz manipulować plikami w czasie działania.
FUSE działa dobrze w przypadku informacji, które można przechowywać w pamięci podręcznej, takich jak strony czy atrybuty, ale powoduje regresję wydajności podczas uzyskiwania dostępu do pamięci zewnętrznej, co jest szczególnie widoczne na urządzeniach ze średniej i niskiej półki. Te regresje są spowodowane łańcuchem komponentów współpracujących w implementacji systemu plików FUSE, a także wieloma przełącznikami 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 do
zmniejszenia kopiowania danych oraz korzystać z interfejsu ContentProvider
API
w celu uzyskania bezpośredniego dostępu do plików niższego systemu plików. Nawet w przypadku tych i innych
optymalizacjioperacje 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 — szczególnie w przypadku operacji odczytu losowego,
w których nie można użyć pamięci podręcznej ani odczytu z wyprzedzeniem. Aplikacje, które mają bezpośredni dostęp do pamięci przez starszą ścieżkę /sdcard/, nadal odczuwają zauważalne spadki wydajności, zwłaszcza podczas wykonywania operacji intensywnie korzystających z 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 przekazywania FUSE
Większość uprawnień dostępu do plików jest sprawdzana podczas otwierania pliku, a dodatkowe sprawdzenia uprawnień są przeprowadzane podczas odczytywania i zapisywania w tym pliku. W niektórych przypadkach można stwierdzić podczas otwierania pliku, ż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 ze 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 żądanie otwarcia 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 czekaniem na odpowiedź demona FUSE w przestrzeni użytkownika na żądania sterownika FUSE.
Poniżej znajdziesz porównanie żądań FUSE i przekazywania FUSE.
Rysunek 3. Żądanie FUSE a żądanie przekazywania FUSE
Gdy aplikacja uzyskuje dostęp do systemu plików FUSE, wykonywane są te operacje:
Sterownik FUSE obsługuje i umieszcza żądanie w kolejce, a następnie przekazuje je do demona FUSE, który obsługuje ten system plików FUSE za pomocą konkretnej instancji połączenia w pliku
/dev/fuse, z którego demon FUSE nie może odczytywać.Gdy demon FUSE otrzyma żądanie otwarcia pliku, decyduje, czy przekazywanie FUSE powinno być dostępne dla tego konkretnego pliku. Jeśli jest dostępne, demon:
Powiadamia sterownik FUSE o tym żądaniu.
Włącza przekazywanie FUSE dla pliku za pomocą ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN, które musi zostać wykonane na deskryptorze pliku otwartego/dev/fuse.
Ioctl otrzymuje (jako parametr) strukturę danych, która zawiera te elementy:
Deskryptor pliku niższego systemu plików, 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 implementacji.
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 odniesienie do pliku niższego systemu plików. Gdy aplikacja zażąda operacji odczytu/zapisu w pliku FUSE, sterownik FUSE sprawdzi, czy odniesienie do pliku niższego systemu plików jest dostępne.
Jeśli odniesienie jest dostępne, sterownik tworzy nowe żądanie wirtualnego systemu plików (VFS) z tymi samymi parametrami, które są kierowane do pliku niższego systemu plików.
Jeśli odniesienie nie jest dostępne, sterownik przekazuje żądanie do demona FUSE.
Powyższe operacje są wykonywane w przypadku odczytu/zapisu i odczytu-iter/zapisu-iter w plikach ogólnych oraz operacji odczytu/zapisu w plikach mapowanych w pamięci. Przekazywanie FUSE dla danego pliku istnieje do momentu zamknięcia tego pliku.
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 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 przekazywanie FUSE było wcześniej włączone, jego wyłączenie uniemożliwi urządzeniu korzystanie z tej funkcji, ale urządzenie pozostanie sprawne.
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 rootadb shell setprop persist.sys.fuse.passthrough.enable {true,false}adb reboot
Aby uzyskać dodatkową pomoc, zapoznaj się z implementacją referencyjną.
Weryfikowanie przekazywania FUSE
Aby sprawdzić, czy MediaProvider używa przekazywania FUSE, poszukaj w logcat komunikatów debugowania. 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 oznacza, że używane jest przekazywanie FUSE.
Pakiet CTS Androida 12 zawiera CtsStorageTest, który obejmuje testy wywołujące przekazywanie FUSE. Aby uruchomić test ręcznie, użyj atest w sposób pokazany poniżej:
atest CtsStorageTest