Sumérjase en SDCardFS: cómo el reemplazo de FUSE de Google reducirá los gastos generales de E / S

Hace varios meses, Google agregó algo llamado " SDCardFS " a las ramas oficiales de AOSP para el kernel de Linux. En ese momento, el movimiento fue notado solo por algunos desarrolladores del kernel, pero por lo demás pasó desapercibido para la mayoría de los usuarios. No es de extrañar teniendo en cuenta el hecho de que la mayoría de los usuarios, incluido yo mismo, no sabemos realmente qué sucede bajo el capó del sistema operativo Android y su núcleo.

Sin embargo, el episodio más reciente del podcast de Android Developers Backstage renovó el interés en este tema. El podcast, presentado por Chet Haase (un ingeniero de software senior en Google), exploró los cambios recientes y futuros realizados en el núcleo. En el programa había un desarrollador de kernel de Linux trabajando en el equipo de Android: Rom Lemarchand. El dúo discutió principalmente qué cambios se hicieron para acomodar las actualizaciones A / B, pero en los últimos 5 minutos del episodio, el Sr. Lemarchand habló sobre "la próxima gran cosa" en la que su equipo estaba trabajando: SDCardFS .

Debo admitir que aprendí sobre la existencia de SDCardFS después de escuchar este podcast. Por supuesto, no fui el único que se interesó en este tema, como lo demostró un hilo reciente de Reddit. Sin embargo, no estaba satisfecho con la explicación básica que se ofrecía en el podcast, y en un esfuerzo por disipar parte de la información errónea que se estaba difundiendo, investigué por mi cuenta y hablé con algunos expertos con conocimiento relevante sobre el importar.

Muchas gracias al desarrollador de software Michal Kowalczyk por contribuir con su conocimiento a este artículo y por tomarse el tiempo para responder mis preguntas.


"Externo" es realmente interno

De buenas a primeras, es probable que haya algunos conceptos erróneos que tenemos que aclarar; de lo contrario, el resto del artículo será muy confuso. Es útil analizar la historia de las tarjetas SD y los teléfonos Android.

En los primeros días de los teléfonos Android, casi todos los dispositivos dependían del uso de sus tarjetas microSD para el almacenamiento. Esto se debió al hecho de que los teléfonos en ese momento se enviaban con minúsculas capacidades de almacenamiento interno. Sin embargo, las tarjetas SD utilizadas para almacenar aplicaciones a menudo no proporcionan una experiencia estelar para el usuario, al menos en comparación con la velocidad con la que la memoria flash interna puede leer / escribir datos. Por lo tanto, el uso creciente de las tarjetas SD para el almacenamiento externo de datos se estaba convirtiendo en una preocupación de la experiencia del usuario para Google.

Debido a la proliferación temprana de las tarjetas SD como dispositivos de almacenamiento externo, las convenciones de nomenclatura de almacenamiento de Android se basaron en el hecho de que cada dispositivo tenía una ranura de tarjeta microSD física real. Pero incluso en dispositivos que no contenían una ranura para tarjeta SD, la etiqueta / sdcard todavía se usaba para señalar el chip de almacenamiento interno real. Más confuso es el hecho de que los dispositivos que utilizaron tanto una tarjeta SD física como un chip de almacenamiento de alta capacidad para el almacenamiento a menudo nombrarían sus particiones basadas en la tarjeta SD. Por ejemplo, en estos dispositivos, el punto de montaje de / sdcard se referiría al chip de almacenamiento interno real, mientras que algo como / storage / sdcard1 se referiría a la tarjeta física externa.

Por lo tanto, a pesar de que la tarjeta microSD se considera prácticamente como un almacenamiento externo, la convención de nomenclatura dio como resultado que la "tarjeta SD" se quedara mucho más allá de cualquier uso real de una tarjeta física. Esta confusión con el almacenamiento también proporcionó algunos dolores de cabeza a los desarrolladores de aplicaciones debido al hecho de que los datos de la aplicación y sus medios se segregaron entre las dos particiones.

El bajo espacio de almacenamiento de los primeros chips de almacenamiento interno hizo que los usuarios descubrieran frustrantemente que ya no podían instalar aplicaciones (debido a que la partición / data está llena). Mientras tanto, sus tarjetas microSD de mayor capacidad se relegaron a contener solo medios (como fotos, música y películas). Los usuarios que exploraron nuestros foros en el pasado podrían recordar estos nombres: Link2SD y Apps2SD. Estas fueron soluciones (raíz) que permitieron a los usuarios instalar sus aplicaciones y sus datos, todo en la tarjeta SD física. Pero estas estaban lejos de ser soluciones perfectas, por lo que Google tuvo que intervenir.

Famosamente, Google desconectó las tarjetas SD desde el principio. El Nexus One sigue siendo el único dispositivo Nexus con una ranura para tarjeta microSD (y siempre lo será, ya que la marca Nexus está efectivamente muerta). Con el Nexus S, ahora solo había una partición unificada para almacenar todos los datos y medios de la aplicación: la partición / data. Lo que antes se conocía como el punto de montaje / sdcard ahora se refería simplemente a un sistema de archivos virtual (implementado bajo el protocolo FUSE como se describe a continuación) ubicado en la partición de datos - / data / media / 0.

Con el fin de mantener la compatibilidad y reducir la confusión, Google aún utilizaba esta partición "sdcard" ahora virtual para almacenar medios. Pero ahora que esta partición virtual de "tarjeta sd" se encontraba realmente dentro de / data, cualquier cosa almacenada dentro de ella contaría para el espacio de almacenamiento del chip de almacenamiento interno. Por lo tanto, correspondía a los OEM considerar cuánto espacio asignar a las aplicaciones (/ datos) versus los medios (/ datos / medios).

Dos "tarjetas SD" muy diferentes

Google esperaba que los fabricantes sigan su ejemplo y se deshagan de las tarjetas SD. Afortunadamente, con el tiempo los fabricantes de teléfonos pudieron obtener estos componentes a capacidades más altas sin dejar de ser rentables, por lo que la necesidad de tarjetas SD comenzaba a disminuir. Pero las convenciones de nomenclatura han persistido para reducir la cantidad de esfuerzo que los desarrolladores y los OEM tendrían que hacer para adaptarse. Actualmente, cuando nos referimos al "almacenamiento externo" nos referimos a una de dos cosas: la tarjeta microSD extraíble real o la partición virtual "SDCard" ubicada en / data / media. El último de estos, prácticamente hablando, es en realidad almacenamiento interno, pero la convención de nomenclatura de Google lo diferencia debido al hecho de que el usuario puede acceder a estos datos (como cuando están conectados a la computadora).

Actualmente, cuando nos referimos al "almacenamiento externo" nos referimos a una de dos cosas: la tarjeta microSD extraíble real o la partición virtual "SDCard" ubicada en / data / media.


La historia de los sistemas de archivos virtuales de Android

Ahora que "sdcard" se trata como un sistema de archivos virtual, significa que podría formatearse como cualquier sistema de archivos que Google quisiera. Comenzando con el Nexus S y Android 2.3, Google eligió formatear "sdcard" como VFAT (FAT virtual). Este movimiento tenía sentido en ese momento, ya que montar VFAT permitiría que casi cualquier computadora acceda a los datos almacenados en su teléfono. Sin embargo, hubo dos problemas principales con esta implementación inicial.

El primero se refiere principalmente al usuario final (usted). Para conectar su dispositivo a su computadora, usaría el modo de almacenamiento masivo USB para transferir datos. Sin embargo, esto requería que el dispositivo Android desmontara la partición virtual antes de que la computadora pudiera acceder a los datos. Si un usuario quisiera usar su dispositivo mientras está enchufado, muchas cosas se mostrarían como no disponibles.

La introducción del Protocolo de transferencia de medios (MTP) resolvió este primer problema. Cuando está enchufado, su computadora ve su dispositivo como un dispositivo de "almacenamiento de medios". Solicita una lista de archivos de su teléfono, y MTP devuelve una lista de archivos que la computadora puede descargar del dispositivo. Cuando se solicita que se elimine un archivo, MTP envía un comando para eliminar el archivo solicitado del almacenamiento. A diferencia del modo de almacenamiento masivo USB que realmente monta la "tarjeta sd", MTP permite al usuario continuar usando su dispositivo mientras está enchufado. Además, el sistema de archivos presente en el teléfono Android ya no importa para que la computadora reconozca los archivos en el dispositivo.

En segundo lugar, estaba el hecho de que VFAT no proporcionaba el tipo de administración de permisos robusta que Google necesitaba. Al principio, muchos desarrolladores de aplicaciones tratarían la "tarjeta sd" como un vertedero para los datos de sus aplicaciones, sin un sentido unificado de dónde almacenar sus archivos. Muchas aplicaciones simplemente crearían una carpeta con el nombre de su aplicación y almacenarían sus archivos allí.

Casi todas las aplicaciones disponibles en ese momento requerían el permiso WRITE_EXTERNAL_STORAGE para escribir sus archivos de aplicación en el almacenamiento externo. Sin embargo, lo que era más preocupante era el hecho de que casi todas las aplicaciones también requerían el permiso READ_EXTERNAL_STORAGE, ¡solo para leer sus propios archivos de datos! Esto significaba que las aplicaciones podían tener acceso fácilmente a los datos almacenados en cualquier lugar del almacenamiento externo, y el usuario a menudo otorgaba tal permiso porque era necesario para que muchas aplicaciones funcionaran incluso.

Google claramente vio esto como problemático. La idea general detrás de la administración de permisos es segregar a qué aplicaciones pueden y no pueden tener acceso. Si a casi todas las aplicaciones se les otorga acceso de lectura a datos de usuario potencialmente confidenciales, entonces el permiso no tiene sentido. Por lo tanto, Google decidió que necesitaban un nuevo enfoque. Ahí es donde entra FUSE.


Sistema de archivos en el espacio de usuario (FUSE)

A partir de Android 4.4, Google decidió dejar de montar la partición virtual "sdcard" como VFAT. En cambio, Google comenzó a usar FUSE para emular FAT32 en la partición virtual "sdcard". Con el programa sdcard llamando a FUSE para emular los permisos de directorio de estilo FAT-on-sdcard, las aplicaciones podrían comenzar a acceder a sus datos almacenados en almacenamiento externo sin requerir ningún permiso . De hecho, comenzando con el nivel 19 de API, READ_EXTERNAL_STORAGE ya no era necesario para acceder a los archivos ubicados en el almacenamiento externo, siempre que la carpeta de datos creada por el demonio FUSE coincida con el nombre del paquete de la aplicación. FUSE se encargaría de sintetizar el propietario, el grupo y los modos de los archivos en el almacenamiento externo cuando se instala una aplicación.

FUSE difiere de los módulos en el núcleo ya que permite a los usuarios sin privilegios escribir sistemas de archivos virtuales. La razón por la que Google implementó FUSE es bastante simple: hizo lo que querían y ya estaba bien entendido y documentado en el mundo de Linux. Para citar a un desarrollador de Google al respecto:

“Debido a que FUSE es una API estable y agradable, esencialmente no se requiere trabajo de mantenimiento cuando se pasa de una versión de kernel a otra. Si migramos a una solución en el núcleo, nos estaríamos registrando para mantener un conjunto de parches para cada versión estable del núcleo ". -Jeff Sharkey, ingeniero de software en Google

Sin embargo, se estaba volviendo bastante claro que la sobrecarga de FUSE estaba introduciendo un éxito en el rendimiento, entre otros problemas. El desarrollador con el que hablé sobre este asunto, Michal Kowalczyk, escribió una excelente publicación de blog hace más de un año que detalla los problemas actuales con FUSE. Se pueden leer más detalles técnicos en su blog, pero describiré sus hallazgos (con su permiso) en términos más simples.


El problema con FUSE

En Android, el daemon de espacio de usuario "sdcard" utiliza FUSE para montar / dev / fuse en el directorio de almacenamiento externo emulado en el arranque. Después de eso, el demonio sdcard sondea el dispositivo FUSE en busca de mensajes pendientes del núcleo. Si escuchó el podcast, es posible que haya escuchado al Sr. Lemarchand referirse a FUSE que introduce gastos generales durante las operaciones de E / S; esto es esencialmente lo que sucede.

En el mundo real, este impacto en el rendimiento afecta a cualquier archivo almacenado en almacenamiento externo.

Problema n. ° 1: gastos generales de E / S

Digamos que creamos un archivo de texto simple, llamado "test.txt", y lo almacenamos en /sdcard/test.txt (que, permítame recordarle, en realidad es /data/media/0/test.txt asumiendo que El usuario actual es el usuario principal en el dispositivo). Si quisiéramos leer (comando cat) este archivo, esperaríamos que el sistema emita 3 comandos: abrir, leer y luego cerrar. De hecho, como demuestra el Sr. Kowalczyk usando strace, eso es lo que sucede:

Pero debido a que el archivo se encuentra en el almacenamiento externo administrado por el demonio sdcard, hay muchas operaciones adicionales que deben realizarse. Según el Sr. Kowalczyk, se necesitan esencialmente 8 pasos adicionales para cada uno de estos 3 comandos individuales :

  1. La aplicación de espacio de usuario emite una llamada al sistema que será manejada por el controlador FUSE en el núcleo (lo vemos en la primera salida de la secuencia)
  2. El controlador FUSE en el kernel notifica al daemon del espacio de usuario (sdcard) sobre una nueva solicitud
  3. El espacio de usuario daemon lee / dev / fuse
  4. El daemon del espacio de usuario analiza el comando y reconoce la operación del archivo (por ejemplo, abrir)
  5. El daemon del espacio de usuario emite una llamada del sistema al sistema de archivos real (EXT4)
  6. Kernel maneja el acceso a datos físicos y los envía de vuelta al espacio de usuario
  7. El espacio de usuario modifica (o no) los datos y los pasa a través de / dev / fuse al kernel nuevamente
  8. Kernel completa la llamada original del sistema y mueve los datos a la aplicación de espacio de usuario real (en nuestro ejemplo cat)

Esto parece una gran sobrecarga para un solo comando de E / S que se ejecutará. Y tú estarías bien. Para demostrar esto, el Sr. Kowalczyk intentó dos pruebas de E / S diferentes: una que implicaba copiar un archivo grande y la otra copiar muchos archivos pequeños. Comparó la velocidad de FUSE (en la partición virtual montada como FAT32) que maneja estas operaciones en comparación con el núcleo (en la partición de datos formateada como EXT4), y descubrió que el FUSE realmente estaba contribuyendo una sobrecarga significativa.

En la primera prueba, copió un archivo de 725 MB en ambas condiciones de prueba. Descubrió que la implementación de FUSE transfirió archivos grandes un 17% más lentamente .

En la segunda prueba, copió 10, 000 archivos, cada uno de ellos de 5 KB de tamaño. En este escenario, la implementación de FUSE fue más lenta en 40 segundos para copiar básicamente 50 MB de datos.

En el mundo real, este impacto en el rendimiento afecta a cualquier archivo almacenado en almacenamiento externo. Esto significa que las aplicaciones como Mapas que almacenan archivos grandes en / tarjeta SD, aplicaciones de música que almacenan toneladas de archivos de música, aplicaciones de cámara y fotos, etc. Cualquier operación de E / S que se realice que implique el almacenamiento externo se ve afectada por la sobrecarga de FUSE. Pero la sobrecarga de E / S no es el único problema con FUSE.

Problema n. ° 2: almacenamiento en caché doble

El almacenamiento en caché de datos es importante para mejorar el rendimiento del acceso a datos. Al almacenar datos esenciales en la memoria, el kernel de Linux puede recuperar rápidamente esos datos cuando sea necesario. Pero debido a la forma en que se implementa FUSE, las tiendas de Android duplican la cantidad de caché que se necesita.

Como lo demuestra el Sr. Kowalczyk, se espera que un archivo de 10 MB se guarde en la memoria caché como exactamente 10 MB, pero en su lugar sube al tamaño de la memoria caché en alrededor de 20 MB. Esto es problemático en dispositivos con menos RAM, ya que las tiendas de kernel de Linux usan caché de página para almacenar datos en la memoria. El Sr. Kowalczyk probó este problema de doble caché utilizando este enfoque:

  1. Cree un archivo con un tamaño conocido (para pruebas, 10 MB)
  2. Copiarlo a / sdcard
  3. Suelte el caché de la página
  4. Tome una instantánea del uso de caché de página
  5. Lee el archivo de prueba
  6. Tome otra instantánea del uso de caché de página

Lo que descubrió fue que antes de su prueba, el kernel estaba utilizando 241 MB para el caché de la página. Una vez que leyó su archivo de prueba, esperaba ver 251MB utilizados para el caché de la página. En cambio, descubrió que ese núcleo estaba usando 263 MB para el caché de la página, aproximadamente el doble de lo esperado . La razón por la que esto ocurre es porque los datos se almacenan primero en la memoria caché por la aplicación del usuario que emitió originalmente la llamada de E / S (FUSE), y en segundo lugar por el demonio sdcard (EXT4 FS).

Problema # 3 - Implementación incompleta de FAT32

Hay dos problemas más derivados del uso de FUSE que emula FAT32 que son menos conocidos en la comunidad de Android.

El primero implica marcas de tiempo incorrectas . Si alguna vez transfirió un archivo (como una foto) y notó que la marca de tiempo es incorrecta, es debido a la implementación de FUSE por parte de Android. Este problema ha existido por años . Para ser más específico, el problema involucra la llamada al sistema utime () que le permite cambiar el tiempo de acceso y modificación de un archivo. Desafortunadamente, las llamadas realizadas al demonio sdcard como usuario estándar no tienen el permiso adecuado para ejecutar esta llamada al sistema. Hay soluciones para esto, pero requieren que tenga acceso de root.

Si alguna vez transfirió un archivo (como una foto) y notó que la marca de tiempo es incorrecta, es debido a la implementación de FUSE por parte de Android.

El siguiente problema es más preocupante para las empresas que usan algo como una tarjeta smartSD. Antes de FUSE, los fabricantes de aplicaciones podían monitorear el indicador O_DIRECT para comunicarse con un microcontrolador incorporado en la tarjeta. Con FUSE, los desarrolladores solo pueden acceder a la versión en caché de un archivo y no pueden ver ningún comando enviado por un microcontrolador. Esto es problemático para algunas aplicaciones empresariales / gubernamentales / bancarias que se comunican con tarjetas microSD de valor agregado.


Dumping FUSE para SDCardFS

Algunos OEMS reconocieron estos problemas desde el principio y comenzaron a buscar una solución en el núcleo para reemplazar FUSE. Samsung, por ejemplo, desarrolló SDCardFS que se basa en WrapFS. Esta solución en el núcleo emula FAT32 al igual que FUSE, pero renuncia a la sobrecarga de E / S, el almacenamiento en caché doble y otros problemas que he mencionado anteriormente. (Sí, permítanme reiterar ese punto, esta solución que Google está implementando ahora se basa en el trabajo de Samsung ).

Los propios Google finalmente han reconocido los inconvenientes asociados con FUSE, por lo que han comenzado a avanzar hacia la capa de emulación FAT32 en el núcleo desarrollada por Samsung. La compañía, como se menciona en el podcast Backstage de desarrolladores de Android, ha estado trabajando para que SDCardFS esté disponible para todos los dispositivos en una próxima versión del kernel. Actualmente puede ver el progreso de su trabajo en AOSP.

Como explicó un desarrollador de Google anteriormente, el mayor desafío con la implementación de una solución en el núcleo es cómo asignar el nombre del paquete a la ID de la aplicación necesaria para que un paquete acceda a sus propios datos en el almacenamiento externo sin requerir ningún permiso. Pero esa declaración se hizo hace un año, y hemos llegado al punto en que el equipo llama a SDCardFS su "próximo gran éxito". Ya han confirmado que el temido error de marca de tiempo se ha solucionado, gracias a alejarse de FUSE, así que podemos esperar ver todos los cambios producidos con el abandono de FUSE.


Conceptos erróneos de verificación de hechos

Si llegaste tan lejos en el artículo, ¡felicitaciones por mantenerte al día con todo hasta ahora! Quería aclarar algunas preguntas que tenía al escribir este artículo:

  • SDCardFS no tiene nada que ver con las tarjetas SD reales . Simplemente se nombra como tal porque maneja el acceso de E / S para / sdcard. Y como recordarán, / sdcard es una etiqueta obsoleta que se refiere al almacenamiento "externo" de su dispositivo (donde las aplicaciones almacenan sus medios).
  • SDCardFS no es un sistema de archivos tradicional como FAT32, EXT4 o F2FS. Es un sistema de archivos de contenedor apilable que pasa comandos a los sistemas de archivos emulados inferiores (en este caso, sería FAT32 en la / sdcard).
  • Nada cambiará con respecto a MTP . Continuará utilizando MTP para transferir archivos a / desde su computadora (hasta que Google se instale en un mejor protocolo). ¡Pero al menos el error de marca de tiempo será corregido!
  • Como se mencionó anteriormente, cuando Google se refiere al "almacenamiento externo", están hablando de la partición FAT32 virtual interna / sdcard (para todos los efectos) o están hablando de una tarjeta microSD real, física y extraíble. La terminología es confusa, pero es lo que nos llama la atención.

Conclusión

Al alejarse de FUSE e implementar una capa de emulación FAT32 en el núcleo (SDCardFS), Google reducirá la sobrecarga de E / S significativa, eliminará el doble almacenamiento en caché y resolverá algunos problemas oscuros relacionados con la emulación de FAT32 de su FUSE.

Dado que estos cambios se realizarán en un kernel, se pueden implementar sin una nueva versión importante de Android. Algunos usuarios esperan ver estos cambios implementados oficialmente en Android 8, pero es posible que cualquier OTA futura en un dispositivo Pixel traiga la versión 4.1 del kernel de Linux en la que Google ha estado trabajando.

Para algunos de ustedes, SDCardFS no es un concepto nuevo. De hecho, los dispositivos Samsung lo han estado utilizando durante años (ellos fueron los que lo desarrollaron después de todo). Desde que SDCardFS se introdujo en AOSP el año pasado, algunos desarrolladores personalizados de ROM y kernel han optado por implementarlo en su trabajo. CyanogenMOD en un momento consideró implementarlo, pero lo retiró cuando los usuarios encontraron problemas con sus fotos. Pero, con suerte, con Google tomando las riendas de este proyecto, los usuarios de Android en todos los dispositivos futuros pueden aprovechar las mejoras introducidas al abandonar FUSE.