2011-01-16 16 views
13

El artículo de Tim Bray "Saving Data Safely" me dejó con preguntas abiertas. Hoy, tengo más de un mes y no he visto ningún seguimiento al respecto, así que decidí abordar el tema aquí.ext4/fsync situación poco clara en Android (Java)

Un punto del artículo es que se debe llamar a FileDescriptor.sync() para estar seguro cuando se utiliza FileOutputStream. Al principio, estaba muy irritado porque nunca había visto ningún código Java sincronizado durante los 12 años que tengo Java. Especialmente porque lidiar con archivos es algo bastante básico. Además, el JavaDoc estándar de FileOutputStream nunca insinuó la sincronización (Java 1.0 - 6). Después de algunas investigaciones, calculé que ext4 podría ser el primer sistema de archivos convencional que requiera sincronización. (¿Hay otros sistemas de archivos en el que se recomienda la sincronización explícita?)

aprecio algunas ideas generales sobre la materia, pero también tengo algunas preguntas específicas:

  1. Cuando se androide hacer la sincronización con el sistema de archivos ? Esto podría ser periódico y adicionalmente basarse en eventos del ciclo de vida (por ejemplo, el proceso de una aplicación pasa al segundo plano).
  2. ¿FileDescriptor.sync() se encarga de sincronizar los metadatos? Eso es sincronizar el directorio del archivo cambiado. Compare con FileChannel.force().
  3. Normalmente, uno no escribe directamente en FileOutputStream. Aquí está mi solución (¿estás de acuerdo?):
     
    FileOutputStream fileOut = ctx.openFileOutput(file, Context.MODE_PRIVATE); 
    BufferedOutputStream out = new BufferedOutputStream(fileOut); 
    try { 
        out.write(something); 
        out.flush(); 
        fileOut.getFD().sync(); 
    } finally { 
        out.close(); 
    } 
    

Respuesta

10

Android va a hacer la sincronización cuando se necesita - por ejemplo, cuando la pantalla se apaga, apagar el dispositivo, etc. Si estás en busca de operación "normal", la sincronización explícita por las aplicaciones nunca es necesaria.

El problema surge cuando el usuario saca la batería de su dispositivo (o hace un restablecimiento completo del kernel), y desea asegurarse de no perder ningún dato.

Entonces, lo primero es darse cuenta: el problema es cuando la energía se pierde repentinamente, por lo que no puede producirse un apagado limpio, y la cuestión de qué va a suceder en el almacenamiento persistente en ese momento.

Si solo está escribiendo un único archivo independiente nuevo, realmente no importa lo que haga. El usuario podría haber extraído la batería mientras estaba escribiendo, justo antes de comenzar a escribir, etc. Si no se sincroniza, significa que hay un tiempo más prolongado desde que terminó de escribir, durante el cual extrae la batería. perderá los datos.

La gran preocupación aquí es cuando desea actualizar un archivo. En ese caso, la próxima vez que lea el archivo que desea tener ya sea los contenido anterior, o los nuevos contenidos. No desea obtener algo escrito a mitad de camino o perder los datos.

Esto a menudo se hace escribiendo los datos en un archivo nuevo, y luego cambiando a los del archivo anterior. Antes de ext4 sabía que, una vez que había terminado de escribir un archivo, las operaciones adicionales en otros archivos no irían al disco hasta que estén en ese archivo, por lo que podría eliminar el archivo anterior o realizar operaciones que dependan de su nuevo archivo estar completamente escrito.

Sin embargo, ahora si escribe el nuevo archivo, borre el anterior y extrae la batería, cuando vuelva a iniciar verá que el archivo anterior se borró y se creó un archivo nuevo, pero el contenido del nuevo archivo sin completar.Al sincronizar, se asegura de que el nuevo archivo esté completamente escrito en ese momento, de modo que puede realizar más cambios (como eliminar el archivo anterior) que dependen de ese estado.

+0

Hubiera esperado que flush() se asegurara de que todo estuviera escrito en el disco, esencialmente llamando a sync(). ¿No es ese el caso? –

+0

@a_horse_with_no_name: No, no lo es. _flush_ y _sync_ son dos operaciones diferentes: flush solo vacía búferes intermedios; sincronización realmente escribe en el almacenamiento. Ver p. http://stackoverflow.com/questions/2340610/difference-between-fflush-and-fsync – sleske

+0

@sleske: gracias por la aclaración! –

1

fileOut.getFD().sync(); debe estar en la cláusula finally, antes del close().

sync() es mucho más importante que close() teniendo en cuenta la durabilidad.

Por lo tanto, cada vez que desee 'terminar' trabajando en un archivo debe sync() hacerlo antes de close() ing it.

posix no garantiza que las escrituras pendientes se escriban en el disco cuando se emite un close().