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 przekazywania danych FUSE, 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 przekazywania danych FUSE.
Urządzenia 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 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 dobrze radzi sobie z informacjami, które można przechowywać w pamięci podręcznej, takimi jak strony lub atrybuty, ale powoduje regres wydajności podczas dostępu do pamięci zewnętrznej. 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 używania interfejsu ContentProvider API do uzyskiwania bezpośredniego dostępu do plików w niższym systemie plików. Nawet przy zastosowaniu tych i innych optymalizacji operacje odczytu i zapisu mogą mieć mniejszą przepustowość, 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
Użycie 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
FUSE był początkowo używany do emulacji pamięci i umożliwienia aplikacjom transparentnego 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 FUSE passthrough.
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 w kole żądanie, a następnie przekazuje je demonowi FUSE, który obsługuje system plików FUSE za pomocą konkretnej instancji połączenia z pliku
/dev/fuse
, którego odczyt jest zablokowany dla demona FUSE.Gdy demon FUSE otrzyma prośbę o otwarcie pliku, zdecyduje, czy przekierowanie FUSE powinno być dostępne dla tego konkretnego pliku. Jeśli jest dostępny, demon:
powiadamia kierowcę FUSE o tym żądaniu.
Umożliwia przekazywanie danych przez FUSE w 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ą:
Deskryptor pliku w niższym 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 wykona żądanie otwarcia, sterownik FUSE obsłuży odpowiedź demona FUSE, a do pliku FUSE w rdzeniu zostanie dodana referencja do niższego pliku systemowego. Gdy aplikacja poprosi o operację odczytu/zapisu pliku FUSE, sterownik FUSE sprawdzi, czy odwołanie do pliku w niższym systemie plików 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, ale kierowane do niższego systemu plików.
Jeśli odwołanie nie jest dostępne, sterownik przekierowuje żądanie do demona FUSE.
Powyższe operacje występują w przypadku odczytu/zapisu i odczytu-iteracji/zapisu-iteracji w przypadku ogólnych plików oraz operacji odczytu/zapisu w przypadku plików zmapowanych na pamięć. Przekazywanie danych 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ń powyższą 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 poprawności przekazywania FUSE
Aby sprawdzić, czy MediaProvider używa przekierowania FUSE, sprawdź logcat
pod kątem wiadomości 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 wskazuje, że przepuszczanie FUSE jest używane.
Pakiet CTS dla Androida 12 zawiera CtsStorageTest
, który obejmuje testy wywołujące przepuszczanie FUSE. Aby przeprowadzić test ręcznie, użyj polecenia test, jak pokazano poniżej:
atest CtsStorageTest