Immergersi in SDCardFS: come la sostituzione di FUSE di Google ridurrà il sovraccarico di I/O

Un'esplorazione approfondita di SDCardFS, il sostituto di FUSE di Google, e di come la sua implementazione ridurrà il sovraccarico di I/O.

Diversi mesi fa, Google ha aggiunto qualcosa chiamato “SDCardFS" ai rami AOSP ufficiali per il kernel Linux. All'epoca il trasloco venne notato solo da alcuni sviluppatori del kernel, ma per il resto è passato sotto il radar della maggior parte degli utenti. Nessuna sorpresa considerando il fatto che la maggior parte degli utenti, me compreso, non sanno veramente cosa succede sotto il cofano del sistema operativo Android e del suo kernel.

Tuttavia, l'episodio più recente del Dietro le quinte degli sviluppatori Android podcast ha rinnovato interesse per questo argomento. Il podcast, ospitato da Chet Haase (un ingegnere software senior di Google), ha esplorato le modifiche recenti e imminenti apportate al kernel. Nello show c'era uno sviluppatore del kernel Linux che lavorava nel team Android: Rom Lemarchand. I due hanno discusso principalmente delle modifiche apportate per accogliere gli aggiornamenti A/B, ma negli ultimi 5 minuti dell'episodio il signor Lemarchand ha parlato della "prossima grande novità" su cui il suo team stava lavorando:

SDCardFS.

Devo ammettere che ho saputo dell'esistenza di SDCardFS dopo aver ascoltato questo podcast. Naturalmente, non ero l'unico a interessarsi a questo argomento, in quanto recente thread Reddit ha mostrato. Tuttavia, non ero soddisfatto della spiegazione di base offerta nel podcast e, nel tentativo di dissipare alcune delle si diffondeva la disinformazione, ho fatto qualche ricerca per conto mio e ho parlato con alcuni esperti con conoscenze pertinenti in merito questione.

Un grande ringraziamento allo sviluppatore di software Michal Kowalczyk per aver contribuito con le sue conoscenze a questo articolo e per aver dedicato del tempo a rispondere alle mie domande.


"Esterno" è davvero interno

Dobbiamo chiarire subito alcuni malintesi, altrimenti il ​​resto dell'articolo risulterà molto confuso. È utile discutere la storia delle schede SD e dei telefoni Android.

Agli albori dei telefoni Android, quasi tutti i dispositivi utilizzavano le proprie schede microSD per l'archiviazione. Ciò era dovuto al fatto che i telefoni all'epoca venivano spediti con minuscole capacità di memoria interna. Tuttavia, le schede SD utilizzate per archiviare le applicazioni spesso non forniscono un'esperienza utente eccezionale, almeno rispetto alla velocità con cui la memoria flash interna può leggere/scrivere i dati. Pertanto, il crescente utilizzo di schede SD per l'archiviazione di dati esterni stava diventando una preoccupazione per l'esperienza utente di Google.

A causa della proliferazione precoce delle schede SD come dispositivi di archiviazione esterni, le convenzioni di denominazione degli archivi di Android erano basate sul fatto che ogni dispositivo aveva uno slot fisico per schede microSD. Ma anche sui dispositivi che non contenevano uno slot per scheda SD, l’etichetta /sdcard veniva comunque utilizzata per indicare l’effettivo chip di archiviazione interno. Ancora più confuso è il fatto che i dispositivi che utilizzavano sia una scheda SD fisica sia un chip di archiviazione ad alta capacità per l'archiviazione spesso denominavano le loro partizioni in base alla scheda SD. Ad esempio, in questi dispositivi il punto di montaggio /sdcard si riferirebbe all'effettivo chip di archiviazione interno, mentre qualcosa come /storage/sdcard1 si riferirebbe alla scheda fisica esterna.

Pertanto, anche se la scheda microSD è praticamente considerata una memoria esterna, la convenzione di denominazione ha fatto sì che "SDCard" persistesse molto tempo dopo qualsiasi utilizzo effettivo di una scheda fisica. Questa confusione con l'archiviazione ha causato anche qualche grattacapo agli sviluppatori di applicazioni a causa del fatto che i dati dell'applicazione e i relativi supporti erano separati tra le due partizioni.

Lo spazio di archiviazione ridotto dei primi chip di archiviazione interna faceva sì che gli utenti scoprissero con frustrazione che non potevano più installare applicazioni (a causa della partizione /data piena). Nel frattempo, le loro schede microSD di maggiore capacità sono state relegate a contenere solo contenuti multimediali (come foto, musica e film). Gli utenti che hanno navigato nei nostri forum in passato potrebbero ricordare questi nomi: Link2SD e Apps2SD. Si trattava di soluzioni (root) che consentivano agli utenti di installare le proprie applicazioni e i relativi dati sulla scheda SD fisica. Ma queste erano tutt’altro che soluzioni perfette, quindi Google ha dovuto intervenire.

Notoriamente, Google ha staccato la spina alle schede SD molto presto. Il Nexus One rimane l'unico dispositivo Nexus con uno slot per schede microSD (e lo sarà per sempre poiché il marchio Nexus è effettivamente morto). Con il Nexus S, ora esisteva una sola partizione unificata per l'archiviazione di tutti i dati e i supporti delle applicazioni: la partizione /data. Quello che una volta era conosciuto come punto di montaggio /sdcard ora si riferiva semplicemente a un filesystem virtuale (implementato sotto FUSIBILE protocollo come discusso di seguito) situato nella partizione dati - /data/media/0.

Per mantenere la compatibilità e ridurre la confusione, Google utilizzava ancora questa partizione "sdcard" ora virtuale per contenere i media. Ma ora che questa partizione virtuale "sdcard" si trovava effettivamente all'interno di /data, qualsiasi cosa archiviata al suo interno verrebbe conteggiata nello spazio di archiviazione del chip di archiviazione interno. Pertanto, spettava agli OEM considerare quanto spazio allocare alle applicazioni (/data) rispetto ai media (/data/media).

Due "schede SD" molto diverse

Google sperava che i produttori seguissero il loro esempio e si sbarazzassero delle schede SD. Per fortuna, nel corso del tempo i produttori di telefoni sono stati in grado di procurarsi questi componenti con capacità più elevate pur rimanendo convenienti, quindi la necessità di schede SD stava cominciando a scarseggiare. Ma le convenzioni di denominazione hanno persistito per ridurre la quantità di sforzi che gli sviluppatori e gli OEM avrebbero dovuto compiere per adattarsi. Attualmente, quando parliamo di “memoria esterna” ci riferiamo a una delle due cose: l'effettiva scheda microSD rimovibile o la partizione virtuale "SDCard" situata in /data/media. Quest'ultimo, in pratica, è in realtà la memoria interna, ma la convenzione di denominazione di Google lo differenzia per il fatto che questi dati sono accessibili all'utente (ad esempio quando sono collegati al computer).

Attualmente, quando parliamo di “memoria esterna” ci riferiamo a una delle due cose: l'effettiva scheda microSD rimovibile o la partizione virtuale "SDCard" situata in /data/media.


La storia dei file system virtuali di Android

Ora che la "sdcard" è trattata come un filesystem virtuale, significa che potrebbe essere formattata come qualsiasi filesystem desiderato da Google. A partire dal Nexus S e Android 2.3, Google ha scelto di formattare la "sdcard" come VFAT (FAT virtuale). Questa mossa aveva senso in quel momento, poiché il montaggio di VFAT avrebbe consentito a quasi tutti i computer di accedere ai dati memorizzati sul telefono. Tuttavia, c’erano due problemi principali con questa implementazione iniziale.

Il primo riguarda principalmente l'utente finale (tu). Per connettere il tuo dispositivo al computer, utilizzerai la modalità di archiviazione di massa USB per trasferire i dati. Ciò, tuttavia, richiedeva che il dispositivo Android smontasse la partizione virtuale prima che il computer potesse accedere ai dati. Se un utente volesse utilizzare il proprio dispositivo mentre è collegato, molte cose verrebbero visualizzate come non disponibili.

IL introduzione del Media Transfer Protocol (MTP) ha risolto questo primo problema. Una volta collegato, il computer vede il tuo dispositivo come un dispositivo di "archiviazione multimediale". Richiede un elenco di file dal telefono e MTP restituisce un elenco di file che il computer può scaricare dal dispositivo. Quando viene richiesta l'eliminazione di un file, MTP invia un comando per rimuovere il file richiesto dall'archivio. A differenza della modalità di archiviazione di massa USB che monta effettivamente la "scheda sd", MTP consente all'utente di continuare a utilizzare il proprio dispositivo mentre è collegato. Inoltre, il file system presente sul telefono Android non è più importante affinché il computer riconosca i file sul dispositivo.

In secondo luogo, c’era il fatto che VFAT non forniva il tipo di solida gestione delle autorizzazioni di cui Google aveva bisogno. All’inizio, molti sviluppatori di applicazioni avrebbero trattato la “sdcard” come una discarica per i dati della loro applicazione, senza un’idea unificata di dove archiviare i propri file. Molte applicazioni creerebbero semplicemente una cartella con il nome dell'app e vi memorizzerebbero i file.

Quasi tutte le applicazioni disponibili all'epoca richiedevano l'estensione SCRIVERE_ARCHIVIAZIONE_ESTERNA il permesso di scrivere i file dell'applicazione nella memoria esterna. Tuttavia, ciò che era più preoccupante era il fatto che quasi tutte le applicazioni richiedevano anche il file READ_ARCHIVIAZIONE_ESTERNA permesso - solo per leggere i propri file di dati! Ciò significava che le applicazioni potevano facilmente accedere ai dati archiviati ovunque nella memoria esterna, e tale autorizzazione veniva spesso concessa dall'utente perché era necessaria per molte app funzione.

Google lo considerava chiaramente problematico. L'idea alla base della gestione delle autorizzazioni è quella di separare ciò a cui le app possono e non possono avere accesso. Se a quasi tutte le app viene concesso l’accesso in lettura a dati utente potenzialmente sensibili, l’autorizzazione non ha senso. Pertanto, Google ha deciso che era necessario un nuovo approccio. È qui che entra in gioco FUSE.


File system nello spazio utente (FUSE)

A partire da Android 4.4, Google ha deciso di non montare più la partizione virtuale “sdcard” come VFAT. Invece, Google ha iniziato a utilizzare FUSE per emulare FAT32 sulla partizione virtuale "sdcard". Con la chiamata del programma sdcard FUSE per emulare i permessi di directory in stile FAT su scheda SD, le applicazioni potrebbero iniziare ad accedere ai dati archiviati su una memoria esterna senza richiedere alcuna autorizzazione. Infatti, a partire dal livello API 19, READ_EXTERNAL_STORAGE non era più necessario per accedere ai file individuati su memoria esterna, a condizione che la cartella dati creata dal demone FUSE corrisponda al nome del pacchetto dell'app. FUSE potrebbe gestire sintetizzando il proprietario, il gruppo e le modalità dei file sulla memoria esterna quando viene installata un'applicazione.

FUSE differisce dai moduli interni al kernel in quanto consente agli utenti non privilegiati di scrivere filesystem virtuali. Il motivo per cui Google ha implementato FUSE è piuttosto semplice: ha fatto quello che volevano e lo faceva già ben compreso e documentato nel mondo di Linux. Per citare a Sviluppatore di Google in merito:

"Poiché FUSE è un'API stabile, non è richiesto praticamente alcun lavoro di manutenzione quando si passa da una versione del kernel all'altra. Se migrassimo a una soluzione in-kernel, ci iscriveremmo per mantenere una serie di patch per ogni versione stabile del kernel." - Jeff Sharkey, Software Engineer presso Google

Tuttavia, stava diventando abbastanza chiaro che, oltre ad altri problemi, le spese generali di FUSE stavano introducendo un calo delle prestazioni. Lo sviluppatore con cui ho parlato di questo argomento, Michal Kowalczyk, ha scritto un eccellente post sul blog più di un anno fa descrivendo in dettaglio i problemi attuali con FUSE. Maggiori dettagli tecnici possono essere letti sul suo blog, ma descriverò le sue scoperte (con il suo permesso) in termini più profani.


Il problema con FUSE

In Android, il demone dello spazio utente "sdcard" utilizza FUSE per montare /dev/fuse nella directory di archiviazione esterna emulata all'avvio. Successivamente, il demone sdcard interroga il dispositivo FUSE per eventuali messaggi in sospeso dal kernel. Se hai ascoltato il podcast, potresti aver sentito il signor Lemarchand fare riferimento a FUSE che introduce un sovraccarico durante le operazioni di I/O: ecco essenzialmente cosa succede.

Nel mondo reale, questo calo delle prestazioni influisce Qualunque file archiviato su una memoria esterna.

Problema n. 1: sovraccarico di I/O

Diciamo che creiamo un semplice file di testo, chiamato “test.txt”, e lo memorizziamo in /sdcard/test.txt (che, lasciamo te lo ricordo, in realtà è /data/media/0/test.txt presupponendo che l'utente corrente sia l'utente principale sul dispositivo). Se volessimo leggere (comando cat) questo file, ci aspetteremmo che il sistema emetta 3 comandi: apri, leggi, quindi chiudi. Infatti, come dimostra il signor Kowalczyk utilizzando strace, ecco cosa succede:

Ma poiché il file si trova nella memoria esterna gestita dal demone sdcard, è necessario eseguire molte operazioni aggiuntive. Secondo Kowalczyk sono necessari essenzialmente 8 passaggi aggiuntivi ciascuno di questi 3 comandi individuali:

  1. L'applicazione Userspace invia una chiamata di sistema che sarà gestita dal driver FUSE nel kernel (lo vediamo nel primo output strace)
  2. Il driver FUSE nel kernel notifica al demone dello spazio utente (sdcard) la nuova richiesta
  3. Il demone dello spazio utente legge /dev/fuse
  4. Il demone dello spazio utente analizza il comando e riconosce l'operazione sul file (es. aprire)
  5. Il demone dello spazio utente invia una chiamata di sistema al filesystem effettivo (EXT4)
  6. Il kernel gestisce l'accesso fisico ai dati e invia i dati allo spazio utente
  7. Lo spazio utente modifica (o meno) i dati e li passa nuovamente attraverso /dev/fuse al kernel
  8. Il kernel completa la chiamata di sistema originale e sposta i dati nell'effettiva applicazione dello spazio utente (nel nostro esempio cat)

Questo sembra molto di sovraccarico solo per un singolo comando I/O da eseguire. E avresti ragione. Per dimostrarlo, Kowalczyk ha tentato due diversi test di I/O: uno prevedeva la copia di un file di grandi dimensioni e l'altro la copia di molti file di piccole dimensioni. Ha confrontato la velocità di FUSE (sulla partizione virtuale montata come FAT32) che gestisce queste operazioni rispetto a quella di kernel (sulla partizione dati formattata come EXT4) e ha scoperto che FUSE stava effettivamente contribuendo in modo significativo sopraelevato.

Nel primo test ha copiato un file da 725 MB in entrambe le condizioni di prova. Ha scoperto che l'implementazione FUSE trasferiva file di grandi dimensioni 17% più lentamente.

Nel secondo test, ha copiato 10.000 file, ciascuno dei quali ha una dimensione di 5 KB. In questo scenario, l’implementazione di FUSE era terminata 40 secondi più lento per copiare sostanzialmente 50 MB di dati.

Nel mondo reale, questo calo delle prestazioni influisce Qualunque file archiviato su una memoria esterna. Ciò significa app come Mappe che memorizzano file di grandi dimensioni su /sdcard, app musicali che memorizzano tonnellate di file musicali, app fotocamera e foto, ecc. Qualsiasi operazione I/O eseguita che coinvolga l'archiviazione esterna è influenzata dal sovraccarico di FUSE. Ma il sovraccarico di I/O non è l’unico problema con FUSE.

Problema n. 2: doppia memorizzazione nella cache

La memorizzazione nella cache dei dati è importante per migliorare le prestazioni di accesso ai dati. Memorizzando dati essenziali in memoria, il kernel Linux è in grado di richiamare rapidamente tali dati quando necessario. Ma a causa del modo in cui è implementato FUSE, Android memorizza il doppio della quantità di cache necessaria.

Come dimostra Kowalczyk, un file da 10 MB dovrebbe essere salvato nella cache esattamente come 10 MB, ma invece aumenta fino alla dimensione della cache di circa 20 MB. Ciò è problematico sui dispositivi con meno RAM, poiché gli archivi del kernel Linux utilizzano la cache delle pagine per archiviare i dati memoria. Kowalczyk ha testato questo problema di doppia memorizzazione nella cache utilizzando questo approccio:

  1. Crea un file con una dimensione nota (per il test, 10 MB)
  2. Copialo in /sdcard
  3. Elimina la cache della pagina
  4. Scatta un'istantanea dell'utilizzo della cache della pagina
  5. Leggi il file di prova
  6. Scatta un'altra istantanea dell'utilizzo della cache della pagina

Ciò che ha scoperto è che prima del test, il kernel utilizzava 241 MB per la cache delle pagine. Una volta letto il file di test, si aspettava di vedere 251 MB utilizzati per la cache della pagina. Invece, ha scoperto che quel kernel stava usando 263 MB per la cache delle pagine - circa il doppio di quanto previsto. Il motivo per cui ciò si verifica è perché i dati vengono prima memorizzati nella cache dall'applicazione utente che originariamente ha emesso la chiamata I/O (FUSE) e poi dal daemon sdcard (EXT4 FS).

Problema n. 3: implementazione incompleta di FAT32

Ci sono altri due problemi derivanti dall'uso di FUSE che emula FAT32 e meno conosciuti nella comunità Android.

Il primo riguarda timestamp errati. Se ti è capitato di trasferire un file (ad esempio una foto) e di notare che il timestamp non è corretto, è a causa dell'implementazione di FUSE su Android. Questo problema ha esisteva per anni. Per essere più specifici, la questione riguarda il utime() chiamata di sistema che consente di modificare l'orario di accesso e di modifica di un file. Sfortunatamente, le chiamate effettuate al demone sdcard come utente standard non hanno l'autorizzazione adeguata per eseguire questa chiamata di sistema. Esistono soluzioni alternative per questo, ma richiedono che tu lo faccia avere accesso root.

Se ti è capitato di trasferire un file (ad esempio una foto) e di notare che il timestamp non è corretto, è a causa dell'implementazione di FUSE su Android.

Il problema successivo è più preoccupante per le aziende che utilizzano qualcosa come a scheda smartSD. Prima di FUSE, i produttori di app potevano monitorare i file Indicatore O_DIRECT per comunicare con un microcontrollore incorporato nella scheda. Con FUSE, gli sviluppatori possono accedere solo alla versione memorizzata nella cache di un file e non sono in grado di vedere alcun comando inviato da un microcontrollore. Ciò è problematico per alcune app aziendali/governative/bancarie che comunicano con schede microSD a valore aggiunto.


Dumping FUSE per SDCardFS

Alcuni OEM hanno riconosciuto subito questi problemi e hanno iniziato a cercare una soluzione interna al kernel per sostituire FUSE. Samsung, ad esempio, ha sviluppato SDCardFS che è basato su WrapFS. Questa soluzione interna al kernel emula FAT32 proprio come fa FUSE, ma rinuncia al sovraccarico di I/O, alla doppia memorizzazione nella cache e ad altri problemi che ho menzionato sopra. (Sì, lasciatemi ribadire questo punto, questa soluzione che Google sta ora implementando si basa sul lavoro di Samsung).

Google stessa ha finalmente riconosciuto gli inconvenienti associati a FUSE, motivo per cui ha iniziato a spostarsi verso il livello di emulazione FAT32 in-kernel sviluppato da Samsung. L'azienda, come menzionato nell'art Dietro le quinte degli sviluppatori Android podcast, ha lavorato per rendere SDCardFS disponibile per tutti i dispositivi in ​​una prossima versione del kernel. Attualmente puoi vedere i progressi del loro lavorare in AOSP.

Come un Lo sviluppatore di Google ha spiegato in precedenza, la sfida più grande con l'implementazione di una soluzione in-kernel è come mappare il nome del pacchetto a ID applicazione necessario affinché un pacchetto possa accedere ai propri dati nella memoria esterna senza richiederne alcuno autorizzazioni. Ma questa affermazione è stata fatta un anno fa e siamo arrivati ​​al punto in cui il team definisce SDCardFS la loro "prossima grande novità". Hanno già confermato che il temuto errore di timestamp è stato corretto, grazie all'abbandono di FUSE, quindi non vediamo l'ora di vedere tutti i cambiamenti apportati con l'abbandono di FUSE.


Idee sbagliate sulla verifica dei fatti

Se sei arrivato fino a questo punto dell'articolo, allora complimenti per aver tenuto il passo con tutto finora! Volevo chiarire alcune domande che mi sono posto mentre scrivevo questo articolo:

  • SDCardFS ha niente a che vedere con le vere e proprie schede SD. Viene chiamato così perché gestisce l'accesso I/O per /sdcard. E come forse ricorderai, /sdcard è un'etichetta obsoleta che si riferisce alla memoria "esterna" del tuo dispositivo (dove le app memorizzano i loro contenuti multimediali).
  • SDCardFS lo è non un file system tradizionale come FAT32, EXT4 o F2FS. È un file system wrapper impilabile che passa i comandi ai file system inferiori ed emulati (in questo caso sarebbe FAT32 sulla /sdcard).
  • Non cambierà nulla rispetto all’MTP. Continuerai a utilizzare MTP per trasferire file da/verso il tuo computer (finché Google non sceglierà un protocollo migliore). Ma almeno l'errore di timestamp verrà corretto!
  • Come accennato in precedenza, quando Google fa riferimento a "Archiviazione esterna" stanno parlando di (a tutti gli effetti scopi) partizione virtuale FAT32 interna /sdcard OPPURE stanno parlando di una microSD reale, fisica e rimovibile carta. La terminologia è confusa, ma è ciò che ci colpisce.

Conclusione

Allontanandosi da FUSE e implementando un livello di emulazione FAT32 in-kernel (SDCardFS), Google ridurrà significativo sovraccarico di I/O, eliminazione della doppia memorizzazione nella cache e risoluzione di alcuni problemi oscuri relativi all'emulazione di FUSE di FAT32.

Poiché queste modifiche verranno apportate a un kernel, potranno essere implementate senza una nuova versione importante di Android accanto ad esso. Alcuni utenti si aspettano di vedere queste modifiche implementate ufficialmente in Android 8, ma è possibile per qualsiasi futuro OTA su un dispositivo Pixel per portare la versione 4.1 del kernel Linux su cui Google ha lavorato SU.

Per alcuni di voi, SDCardFS non è un concetto nuovo. In effetti, i dispositivi Samsung lo utilizzano da anni (dopo tutto sono stati loro a svilupparlo). Da quando SDCardFS è stato introdotto in AOSP lo scorso anno, alcuni sviluppatori di ROM e kernel personalizzati hanno scelto di implementarlo nel loro lavoro. CyanogenMOD a un certo punto ha preso in considerazione l'implementazione, ma l'ha annullato quando gli utenti hanno riscontrato problemi con le loro foto. Ma si spera che con Google che prenderà le redini di questo progetto, gli utenti Android su tutti i dispositivi futuri potranno trarre vantaggio dai miglioramenti introdotti con l'abbandono di FUSE.