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

Una exploración en profundidad de SDCardFS, el reemplazo de Google para FUSE, y cómo su implementación reducirá la sobrecarga de E/S.

Hace varios meses, Google añadió algo llamado "Tarjeta SDFS”a las ramas oficiales de AOSP para el kernel de Linux. En aquel momento, el movimiento sólo fue notado 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, incluyéndome a mí, no sabemos realmente lo que sucede bajo el capó del sistema operativo Android y su núcleo.

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

Tarjeta SDFS.

Debo admitir que me enteré de la existencia de SDCardFS después de escuchar este podcast. Por supuesto, no fui el único que se interesó en este tema, como hilo reciente de Reddit ha mostrado. Sin embargo, no estaba satisfecho con la explicación básica que se ofrecía en el podcast y, en un esfuerzo por disipar algunas de las dudas, Se está difundiendo información errónea, investigué un poco por mi cuenta y hablé con algunos expertos con conocimientos relevantes sobre el tema. asunto.

Muchas gracias al desarrollador de software Michal Kowalczyk por aportar sus conocimientos a este artículo y por tomarse el tiempo para responder mis preguntas.


"Externo" es realmente interno

Desde el principio, es probable que haya algunos conceptos erróneos que debemos 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 tarjetas microSD para almacenamiento. Esto se debió al hecho de que los teléfonos en ese momento se vendían con capacidades de almacenamiento interno minúsculas. Sin embargo, las tarjetas SD utilizadas para almacenar aplicaciones a menudo no brindan una experiencia de usuario excelente, al menos en comparación con la velocidad con la que la memoria flash interna puede leer/escribir datos. Por lo tanto, el uso cada vez mayor de tarjetas SD para almacenamiento externo de datos se estaba convirtiendo en una preocupación para Google en materia de experiencia del usuario.

Debido a la proliferación temprana de tarjetas SD como dispositivos de almacenamiento externo, las convenciones de nomenclatura de almacenamiento de Android se basaban en el hecho de que cada dispositivo tenía una ranura para 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 utilizaban tanto una tarjeta SD física como un chip de almacenamiento de alta capacidad para el almacenamiento a menudo nombrarían sus particiones en función de la tarjeta SD. Por ejemplo, en estos dispositivos el punto de montaje /sdcard se referiría al chip de almacenamiento interno real, mientras que algo como /storage/sdcard1 se referiría a la tarjeta externa física.

Por lo tanto, aunque la tarjeta microSD prácticamente se considera almacenamiento externo, la convención de nomenclatura dio como resultado que la “Tarjeta SD” permaneciera mucho tiempo después 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 estaban segregados entre las dos particiones.

El poco espacio de almacenamiento de los primeros chips de almacenamiento interno hacía que los usuarios descubrieran frustrantemente que ya no podían instalar aplicaciones (debido a que la partición /data estaba llena). Mientras tanto, sus tarjetas microSD de mayor capacidad quedaron relegadas a contener únicamente medios (como fotos, música y películas). Los usuarios que navegaron por nuestros foros en el pasado pueden recordar estos nombres: Link2SD y Apps2SD. Se trataba de soluciones (raíz) que permitían a los usuarios instalar sus aplicaciones y sus datos en la tarjeta SD física. Pero estas estaban lejos de ser soluciones perfectas, por lo que Google tuvo que intervenir.

Es famoso que Google desconectó las tarjetas SD desde el principio. El Nexus One sigue siendo el único dispositivo Nexus con una ranura para tarjetas microSD (y lo será para siempre, 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 las aplicaciones: la partición /data. Lo que alguna vez se conoció como el punto de montaje /sdcard ahora simplemente se refería a un sistema de archivos virtual (implementado bajo el FUSIBLE protocolo como se analiza a continuación) ubicado en la partición de datos - /data/media/0.

Para mantener la compatibilidad y reducir la confusión, Google todavía usó esta partición "tarjeta SD" ahora virtual para almacenar medios. Pero ahora que esta partición virtual “sdcard” estaba realmente ubicada dentro de /data, cualquier cosa almacenada dentro de ella contaría contra 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 (/data) frente a los medios (/data/media).

Dos "tarjetas SD" muy diferentes

Google esperaba que los fabricantes siguieran su ejemplo y se deshicieran de las tarjetas SD. Afortunadamente, con el tiempo los fabricantes de teléfonos pudieron obtener estos componentes en mayores capacidades sin dejar de ser rentables, por lo que la necesidad de tarjetas SD comenzó 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 a “almacenamiento externo” nos referimos a cualquiera de dos cosas: la tarjeta microSD extraíble real o la partición virtual “SDCard” ubicada en /data/media. El último de ellos, en la práctica, en realidad es almacenamiento interno, pero la convención de nomenclatura de Google lo diferencia debido al hecho de que el usuario puede acceder a estos datos (por ejemplo, cuando está conectado a la computadora).

Actualmente, cuando nos referimos a “almacenamiento externo” nos referimos a cualquiera 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 la “tarjeta sd” se trata como un sistema de archivos virtual, significaba que se podía formatear como cualquier sistema de archivos que Google quisiera. A partir del Nexus S y Android 2.3, Google optó por formatear la “tarjeta SD” como VFAT (FAT virtual). Esta medida tenía sentido en ese momento, ya que montar VFAT permitiría que casi cualquier computadora accediera a los datos almacenados en su teléfono. Sin embargo, hubo dos problemas importantes con esta implementación inicial.

El primero se refiere principalmente al usuario final (usted). Para conectar su dispositivo a su computadora, deberá utilizar 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á conectado, muchas cosas se mostrarían como no disponibles.

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

En segundo lugar, estaba el hecho de que VFAT no proporcionaba el tipo de gestión de permisos sólida que Google necesitaba. Al principio, muchos desarrolladores de aplicaciones tratarían la “tarjeta SD” como un vertedero de 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 la aplicación y almacenarían sus archivos allí.

Casi todas las aplicaciones que existían en ese momento requerían la ESCRIBIR_ALMACENAMIENTO_EXTERNO permiso para escribir los archivos de su 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 la READ_EXTERNAL_STORAGE permiso - ¡sólo para leer sus propios archivos de datos! Esto significó que las aplicaciones podrían tener acceso fácilmente a los datos almacenados en cualquier lugar del almacenamiento externo. y dicho permiso a menudo lo concedía el usuario porque era necesario para que muchas aplicaciones incluso función.

Google claramente vio esto como problemático. La idea detrás de la gestió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 tanto, Google decidió que necesitaba un nuevo enfoque. Ahí es donde entra en juego FUSE.


Sistema de archivos en el espacio de usuario (FUSE)

A partir de Android 4.4, Google decidió ya no montar la partición virtual "sdcard" como VFAT. En cambio, Google comenzó a utilizar FUSE para emular FAT32 en la partición virtual "sdcard". Con el programa sdcard llamando FUSE para emular permisos de directorio estilo FAT-on-sdcard, las aplicaciones podrían comenzar a acceder a sus datos almacenados en un almacenamiento externo sin requerir ningún permiso. De hecho, a partir del nivel API 19, READ_EXTERNAL_STORAGE ya no era necesario para acceder a los archivos ubicados en 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 sintetizar el propietario, el grupo y los modos de los archivos en el almacenamiento externo cuando se instala una aplicación.

FUSE se diferencia de los módulos internos en que permite a 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 un Desarrollador de Google al respecto:

"Debido a que FUSE es una API agradable y estable, prácticamente no se requiere ningún trabajo de mantenimiento al pasar de una versión del kernel a otra. Si migramos a una solución integrada en el kernel, nos registraríamos para mantener un conjunto de parches para cada versión estable del kernel". -Jeff Sharkey, ingeniero de software de Google

Sin embargo, cada vez estaba más claro que los gastos generales de FUSE estaban afectando el rendimiento, entre otras cuestiones. 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 detallando 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 sencillos.


El problema con FUSIBLE

En Android, el demonio del espacio de usuario “sdcard” utiliza FUSE para montar /dev/fuse en el directorio de almacenamiento externo emulado durante el arranque. Después de eso, el demonio sdcard sondea el dispositivo FUSE en busca de mensajes pendientes del kernel. 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 cualquier archivo almacenado en un almacenamiento externo.

Problema nº1: sobrecarga de E/S

Digamos que creamos un archivo de texto simple, llamado “test.txt”, y lo almacenamos en /sdcard/test.txt (que, por ejemplo, Le recuerdo que en realidad es /data/media/0/test.txt suponiendo que el usuario actual es el usuario principal en el dispositivo). Si quisiéramos leer (comando cat) este archivo, esperaríamos que el sistema emitiera 3 comandos: abrir, leer y luego cerrar. De hecho, como demuestra el Sr. Kowalczyk utilizando rastro, eso es lo que pasa:

Pero debido a que el archivo está ubicado en el almacenamiento externo administrado por el demonio sdcard, hay muchas operaciones adicionales que deben realizarse. Según el Sr. Kowalczyk, se necesitan esencialmente ocho 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 kernel (lo vemos en la primera salida de strace)
  2. El controlador FUSE en el kernel notifica al demonio del espacio de usuario (sdcard) sobre una nueva solicitud
  3. El demonio del espacio de usuario lee /dev/fuse
  4. El demonio del espacio de usuario analiza el comando y reconoce la operación del archivo (ej. abierto)
  5. El demonio del espacio de usuario emite una llamada al sistema al sistema de archivos real (EXT4)
  6. El 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. El kernel completa la llamada al sistema original y mueve los datos a la aplicación del espacio de usuario real (en nuestro ejemplo cat)

Esto parece mucho de gastos generales solo para ejecutar un único comando de E/S. 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 versus la kernel (en la partición de datos formateada como EXT4), y descubrió que FUSE estaba contribuyendo significativamente gastos generales.

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

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 había terminado. 40 segundos más lento copiar básicamente 50 MB de datos.

En el mundo real, este impacto en el rendimiento afecta cualquier archivo almacenado en un almacenamiento externo. Esto significa que aplicaciones como Mapas almacenan archivos grandes en /sdcard, 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 lleve a cabo y que involucre 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: doble almacenamiento en caché

El almacenamiento en caché de datos es importante para mejorar el rendimiento del acceso a los 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, Android almacena el doble 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é con exactamente 10 MB, pero en lugar de eso aumenta hasta el tamaño de la memoria caché. en alrededor de 20 MB. Esto es problemático en dispositivos con menos RAM, ya que las tiendas del kernel de Linux utilizan el caché de páginas para almacenar datos en memoria. Kowalczyk probó este problema de doble caché utilizando este enfoque:

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

Lo que encontró 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 251 MB utilizados para el caché de la página. En cambio, descubrió que ese núcleo estaba usando 263 MB para caché de página - acerca de el doble de lo esperado. La razón por la que esto ocurre es porque los datos son almacenados en caché primero por la aplicación de usuario que originalmente emitió la llamada de E/S (FUSE) y, en segundo lugar, por el demonio sdcard (EXT4 FS).

Problema nº 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 involucra marcas de tiempo incorrectas. Si alguna vez transfirió un archivo (como una foto) y notó que la marca de tiempo es incorrecta, se debe a la implementación de FUSE en Android. Este problema tiene existió para años. Para ser más específico, la cuestión involucra la utime() llamada al sistema 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. Existen soluciones para esto, pero requieren que usted tener acceso root.

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

El siguiente problema es más preocupante para las empresas que utilizan algo como un tarjeta SD inteligente. Antes de FUSE, los creadores de aplicaciones podían monitorear el Bandera O_DIRECT para comunicarse con un microcontrolador integrado en la tarjeta. Con FUSE, los desarrolladores solo pueden acceder a la versión almacenada 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.


Volviendo FUSE para SDCardFS

Algunos OEM reconocieron estos problemas desde el principio y comenzaron a buscar una solución interna para reemplazar FUSE. Samsung, por ejemplo, desarrolló Tarjeta SDFS que se basa en WrapFS. Esta solución integrada en el kernel emula FAT32 tal como lo hace FUSE, pero evita la sobrecarga de E/S, el doble almacenamiento en caché y otros problemas que mencioné anteriormente. (Sí, permítanme reiterar ese punto, esta solución que ahora está implementando Google se basa en el trabajo de Samsung).

Los propios Google finalmente han reconocido los inconvenientes asociados con FUSE, razón por la cual han comenzado a avanzar hacia la capa de emulación FAT32 en el kernel desarrollada por Samsung. La empresa, como se menciona en el Desarrolladores de Android entre bastidores podcast, ha estado trabajando para que SDCardFS esté disponible para todos los dispositivos en una próxima versión del kernel. Actualmente puedes ver el progreso de su trabajar en AOSP.

Como un El desarrollador de Google explicó anteriormente, el mayor desafío al implementar una solución en el kernel es cómo asignar el nombre del paquete a ID de aplicación necesario para que un paquete acceda a sus propios datos en el almacenamiento externo sin necesidad de ningún permisos. Pero esa declaración se hizo hace un año y hemos llegado al punto en el que el equipo llama a SDCardFS su "próxima gran novedad". Ya han confirmado que el temido error de marca de tiempo se ha solucionado gracias al alejamiento de FUSE, por lo que podemos esperar ver todos los cambios producidos con el abandono de FUSE.


Conceptos erróneos sobre la verificación de hechos

Si has llegado hasta aquí en el artículo, ¡felicitaciones por mantenerte al día con todo hasta ahora! Quería aclarar algunas preguntas que tuve al escribir este artículo:

  • SDCardFS tiene nada que ver con tarjetas SD reales. Simplemente se llama así porque maneja el acceso de E/S para /sdcard. Y como recordarás, /sdcard es una etiqueta obsoleta que se refiere al almacenamiento "externo" de tu dispositivo (donde las aplicaciones almacenan sus medios).
  • SDCardFS es no es un sistema de archivos tradicional como FAT32, EXT4 o F2FS. Es un sistema de archivos contenedor apilable que pasa comandos a los sistemas de archivos emulados inferiores (en este caso, sería FAT32 en /sdcard).
  • Nada cambiará con respecto a MTP. Continuará usando MTP para transferir archivos hacia y desde su computadora (hasta que Google elija un protocolo mejor). ¡Pero al menos se solucionará el error de marca de tiempo!
  • Como se mencionó anteriormente, cuando Google se refiere a "Almacenamiento externo", se refiere a (para todos los efectos) propósitos) partición FAT32 virtual interna/tarjeta SD O están hablando de una microSD real, física y extraíble tarjeta. 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 kernel (SDCardFS), Google reducirá importante sobrecarga de E/S, eliminando el doble almacenamiento en caché y resolviendo algunos problemas oscuros relacionados con la emulación de FUSE de FAT32.

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

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