2011-01-16 10 views
13

Tim Brays Artikel "Saving Data Safely" ließ mich mit offenen Fragen. Heute ist es mehr als einen Monat alt und ich habe keine Nachfolge gesehen, also habe ich beschlossen, das Thema hier anzusprechen.ext4/fsync Situation unklar in Android (Java)

Ein Punkt des Artikels ist, dass FileDescriptor.sync() aufgerufen werden sollte, um bei der Verwendung von FileOutputStream auf der sicheren Seite zu sein. Zuerst war ich sehr irritiert, weil ich in den 12 Jahren, in denen ich Java mache, nie einen Java-Code gesehen habe, der eine Synchronisierung durchführt. Vor allem, da das Arbeiten mit Dateien eine ziemlich einfache Sache ist. Auch das Standard-JavaDoc von FileOutputStream hat nie auf eine Synchronisierung hingewiesen (Java 1.0 - 6). Nach einigen Nachforschungen stellte ich fest, dass ext4 möglicherweise das erste Mainstream-Dateisystem ist, das synchronisiert werden muss. (Gibt es andere Dateisysteme, bei denen explizite Synchronisierung geraten ist?)

ich einige allgemeine Gedanken über die Angelegenheit zu schätzen wissen, aber ich habe auch einige spezifische Fragen:

  1. Wann wird Android tun, um die Synchronisierung auf das Dateisystem ? Dies könnte periodisch sein und zusätzlich auf Lebenszyklusereignissen basieren (z. B. geht der Prozess einer Anwendung in den Hintergrund).
  2. Bearbeitet FileDescriptor.sync() die Synchronisierung der Metadaten? Das synchronisiert das Verzeichnis der geänderten Datei. Vergleichen Sie mit FileChannel.force().
  3. Normalerweise schreibt man nicht direkt in den FileOutputStream. Hier ist meine Lösung (sind Sie einverstanden?):
     
    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(); 
    } 
    

Antwort

10

Android wird die Synchronisierung durchführen, wenn es muss - wie wenn der Bildschirm schaltet sich aus, Abschaltung des Gerätes, etc. Wenn Sie gerade betrachten "normale" Operation, explizite Synchronisierung von Anwendungen wird nie benötigt.

Das Problem tritt auf, wenn der Benutzer die Batterie aus dem Gerät zieht (oder einen Hard Reset des Kernels ausführt), und Sie sicherstellen möchten, dass Sie keine Daten verlieren.

Also die erste Sache zu realisieren: Das Problem ist, wenn die Stromversorgung plötzlich verloren ist, so dass eine saubere Abschaltung nicht passieren kann, und die Frage, was wird in diesem Zeitpunkt an persistenten Speicher geschehen.

Wenn Sie nur eine einzige unabhängige neue Datei schreiben, spielt es keine Rolle, was Sie tun. Der Benutzer könnte die Batterie während des Schreibvorgangs gezogen haben, direkt bevor Sie mit dem Schreiben begonnen haben, usw. Wenn Sie nicht synchronisieren, bedeutet dies nur, dass Sie etwas länger brauchen, wenn Sie mit dem Schreiben der Batterie fertig sind wird die Daten verlieren.

Das große Problem hier ist, wenn Sie eine Datei aktualisieren möchten. In diesem Fall, wenn Sie als nächstes die Datei lesen möchten Sie entweder die vorherigen Inhalt haben, oder die neuen Inhalte. Sie möchten nichts auf halbem Wege schreiben oder die Daten verlieren.

Dies geschieht oft, indem die Daten in eine neue Datei geschrieben werden und dann zu der alten Datei gewechselt wird. Vor ext4 wussten Sie, dass, sobald Sie mit dem Schreiben einer Datei fertig waren, weitere Operationen auf anderen Dateien erst auf der Festplatte ausgeführt wurden, so dass Sie die vorherige Datei sicher löschen oder Operationen ausführen konnten, die von Ihrer neuen Datei abhängig waren vollständig geschrieben werden.

Jetzt aber, wenn Sie die neue Datei schreiben, dann löschen Sie die alte, und der Akku wird gezogen, beim nächsten Booten können Sie sehen, dass die alte Datei gelöscht und neue Datei erstellt, aber der Inhalt der neuen Datei ist unvollständig.Indem Sie die Synchronisierung durchführen, stellen Sie sicher, dass die neue Datei zu diesem Zeitpunkt vollständig geschrieben ist und weitere Änderungen (z. B. das Löschen der alten Datei) vornehmen kann, die von diesem Status abhängen.

+0

Ich hätte erwartet, dass die flush() sicherstellen, dass alles auf die Disc geschrieben wird - im Wesentlichen durch den Aufruf von sync(). Ist das nicht der Fall? –

+0

@a_horse_with_no_name: Nein, ist es nicht. _flush_ und _sync_ sind zwei verschiedene Operationen: flush spült nur Zwischenpuffer; Die Synchronisierung schreibt tatsächlich in den Speicher. Siehe z.B. http://stackoverflow.com/questions/2340610/difference-between-fflush-and-fsync – sleske

+0

@sleske: Danke für die Klarstellung! –

1

fileOut.getFD().sync(); sollte auf der endgültigen Klausel sein, vor der close().

sync() ist viel wichtiger als close() unter Berücksichtigung der Haltbarkeit.

Also jedes Mal, wenn Sie "arbeiten" an einer Datei beenden möchten, sollten Sie sync() es vor close() ing es.

posix garantiert nicht, dass ausstehende Schreibvorgänge auf die Festplatte geschrieben werden, wenn Sie eine close() ausgeben.