Android 12 obsługuje przekierowanie FUSE, które minimalizuje narzut FUSE, aby osiągnąć wydajność porównywaną z dostępem bezpośrednim do niższego systemu plików. Przekazywanie danych przez 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 oraz wersji Androida, na której działa:
Urządzenia, które są aktualizowane z Androida 11 do Androida 12, nie mogą obsługiwać funkcji FUSE passthrough, ponieważ jądra tych urządzeń są zamrożone i nie można przejść na jądro, które zostało oficjalnie zaktualizowane o zmiany dotyczące FUSE passthrough.
Urządzenia z Androidem 12 mogą obsługiwać przekierowanie FUSE, jeśli używają oficjalnego jądra. W przypadku takich urządzeń kod frameworka Androida, który implementuje przekierowanie FUSE, jest umieszczony 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), mogą również uzyskiwać dostęp do zmian w MediaProvider, ponieważ są one udostępniane publicznie.
FUSE a SDCardFS
System plików w przestrzeni użytkownika (FUSE) to mechanizm, który umożliwia przeniesienie operacji wykonywanych na systemie plików FUSE do jądra (sterownika FUSE) w programie w przestrzeni użytkownika (demonie FUSE), który wdraża 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łasnego demona FUSE, aby przechwytywać dostęp do plików, stosować dodatkowe funkcje zabezpieczeń i ochrony prywatności oraz manipulować plikami w czasie wykonywania.
FUSE działa dobrze w przypadku informacji, które można przechowywać w pamięci podręcznej, takich jak strony lub atrybuty, ale powoduje regresję wydajności podczas uzyskiwania dostępu do zewnętrznej pamięci masowej. Jest to szczególnie widoczne na urządzeniach ze średniej i niższej półki. Te regresje są spowodowane przez łańcuch komponentów współpracujących w ramach 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 prostszy i w pełni zaimplementowany w jądrze).
Aby ograniczyć te regresje, aplikacje mogą używać złączania w celu zmniejszenia kopiowania danych i korzystać z interfejsu ContentProvider API, aby uzyskać bezpośredni dostęp do plików w niższym systemie plików. Nawet przy zastosowaniu tych i innych optymalizacji operacje odczytu i zapisu mogą korzystać z mniejszej przepustowości, gdy używa się FUSE w porównaniu z bezpośrednim dostępem do niższego systemu plików  –  zwłaszcza w przypadku operacji losowego odczytu, w których nie pomagają buforowanie ani odczyt z wyprzedzeniem. Aplikacje, które bezpośrednio uzyskują dostęp do pamięci masowej za pomocą starszego interfejsu /sdcard/
, nadal notują zauważalne spadki wydajności, zwłaszcza podczas wykonywania operacji intensywnie wykorzystujących wejścia/wyjścia.
Prośby o przestrzeń użytkownika SDcardFS
Korzystanie z SDcardFS może przyspieszyć emulację pamięci i sprawdzanie uprawnień FUSE, ponieważ usuwa wywołanie 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. Prośby o przestrzeń użytkownika SDcardFS
Prośby dotyczące przestrzeni użytkownika FUSE
Funkcja FUSE była początkowo używana do emulacji pamięci i umożliwienia aplikacjom przejrzystego korzystania z pamięci wewnętrznej lub zewnętrznej karty SD. Korzystanie z FUSE powoduje pewien narzut, ponieważ każde żądanie w przestrzeni użytkownika przechodzi przez ścieżkę: Przestrzeń użytkownika → VFS → sterownik FUSE → demon FUSE → VFS → ext4 → pamięć podręczna strony/miejsce na dane.
Rysunek 2. Prośby dotyczące przestrzeni użytkownika FUSE
Żądania przepuszczania FUSE
Większość uprawnień dostępu do plików jest sprawdzana w momencie ich otwierania, a dodatkowe sprawdzanie uprawnień odbywa się podczas odczytu i zapisu do pliku. W niektórych przypadkach w momencie otwierania pliku można wiedzieć, że aplikacja, która wysłała żądanie, ma pełny dostęp do tego pliku, więc system nie musi przekierowywać żądań odczytu i zapisu z sterownika FUSE do demona FUSE (ponieważ oznaczałoby to tylko przenoszenie danych z jednego miejsca do drugiego).
W przypadku przekazywania przez 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ć przekazywane bezpośrednio do niższego systemu plików. Dzięki temu nie trzeba czekać na odpowiedź demona FUSE w przestrzeni użytkownika na prośby sterownika FUSE.
Poniżej znajdziesz porównanie żądań FUSE i żądań przelotowych FUSE.
Rysunek 3. Żądanie FUSE a żądanie przepuszczania FUSE
Gdy aplikacja uzyskuje dostęp do systemu plików FUSE, wykonuje te operacje:
Sterownik FUSE obsługuje i umieszcza prośbę w kole, a następnie przekazuje ją demonowi FUSE, który obsługuje system plików FUSE za pomocą konkretnego wystąpienia połączenia z pliku
/dev/fuse
, którego odczyt jest zablokowany przez demona FUSE.Gdy demon FUSE otrzyma prośbę o otwarcie pliku, decyduje, czy w przypadku tego konkretnego pliku ma być dostępna opcja FUSE passthrough. Jeśli jest dostępny, demon:
powiadamia kierowcę FUSE o tym żądaniu.
Umożliwia przekazywanie danych przez FUSE do pliku za pomocą ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, który musi być wykonany na deskryptorze pliku otwartego/dev/fuse
.
ioctl otrzymuje (jako parametr) strukturę danych zawierającą:
Descriptor pliku w dolnym systemie plików, który jest celem funkcji przekazywania.
Unikalny identyfikator żądania FUSE, które jest obecnie obsługiwane (musi być otwarte lub w trakcie tworzenia).
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 wypełnia żądanie otwarcia, sterownik FUSE obsługuje odpowiedź demona FUSE, a do pliku FUSE w rdzeniu dodawane jest odwołanie do niższego pliku systemowego. Gdy aplikacja poprosi o operację odczytu/zapisu pliku FUSE, sterownik FUSE sprawdzi, czy odwołanie do pliku systemu plików niższego poziomu jest dostępne.
Jeśli odwołanie jest dostępne, sterownik tworzy nowe żądanie systemu plików VFS (Virtual File System) z tymi samymi parametrami, które kierują do niższego systemu plików.
Jeśli odwołanie jest niedostępne, sterownik przekierowuje żądanie do demona FUSE.
Powyższe operacje występują w przypadku odczytu/zapisu oraz odczytu/zapisu iteracyjnego w przypadku ogólnych plików i operacji odczytu/zapisu w przypadku plików mapowanych na pamięć. Przekazywanie danych przez FUSE w przypadku danego pliku istnieje dopóki plik nie zostanie zamknięty.
Implementowanie przekazywania FUSE
Aby włączyć przekierowanie 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ń tę zmianę konfiguracji lub ustaw wartość persist.sys.fuse.passthrough.enable
na false
. Jeśli wcześniej włączysz przekierowanie FUSE, wyłączenie tej funkcji spowoduje, że urządzenie nie będzie używać przekierowania FUSE, ale urządzenie pozostanie sprawne.
Aby włączyć lub wyłączyć przepuszczanie FUSE bez flashowania urządzenia, zmień właściwość systemu za pomocą poleceń ADB. Poniżej znajdziesz przykład.
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Więcej informacji znajdziesz w implementacji referencyjnej.
Sprawdzanie przekierowania FUSE
Aby sprawdzić, czy MediaProvider używa przekierowania FUSE, sprawdź logcat
pod kątem wiadomości 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...
Wpis FuseDaemon: Using FUSE passthrough
w logu wskazuje, że przepuszczanie FUSE jest używane.
Pakiet CTS dla Androida 12 zawiera CtsStorageTest
, który obejmuje testy uruchamiające przepuszczanie FUSE. Aby przeprowadzić test ręcznie, użyj polecenia test, jak pokazano poniżej:
atest CtsStorageTest