Przekazywanie FUSE

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.

FUSE Passthrough SDcardFS

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.

Przekazywanie FUSE

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.

Porównanie przekazywania FUSE

Rysunek 3. Żądanie FUSE a żądanie przekazywania FUSE

Gdy aplikacja uzyskuje dostęp do systemu plików FUSE, wykonywane są te operacje:

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

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

    1. Powiadamia sterownik FUSE o tym żądaniu.

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

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

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