Przejście FUSE

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ęć.

FUSE Passthrough SDcardFS

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.

BEZPIECZNIK BEZPIECZNIK przelotowy

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.

Porównanie przejścia 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:

  1. 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.

  2. 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:

    1. Powiadamia sterownik FUSE o tym żądaniu.

    2. 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 .

  3. 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ń.

  4. 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