2011-01-16 18 views
13

L'articolo di Tim Bray "Saving Data Safely" mi ha lasciato con domande aperte. Oggi ha più di un mese di vita e non ho visto alcun follow-up, quindi ho deciso di affrontare l'argomento qui.situazione ext4/fsync non chiara in Android (Java)

Un punto dell'articolo è che FileDescriptor.sync() deve essere chiamato per essere al sicuro quando si utilizza FileOutputStream. All'inizio ero molto irritato, perché non ho mai visto alcun codice Java fare una sincronizzazione durante i 12 anni in cui lo faccio. Soprattutto dal momento che affrontare i file è una cosa piuttosto semplice. Inoltre, lo standard JavaDoc di FileOutputStream non ha mai accennato alla sincronizzazione (Java 1.0 - 6). Dopo alcune ricerche, ho capito che ext4 potrebbe essere effettivamente il primo file system mainstream che richiede la sincronizzazione. (Ci sono altri file system in cui è consigliato sincronizzazione esplicita?)

apprezzo alcune riflessioni generali in materia, ma ho anche alcune domande specifiche:

  1. Quando sarà Android fare la sincronizzazione al file system ? Questo potrebbe essere periodico e in aggiunta basato su eventi del ciclo di vita (ad esempio, il processo di un'app passa in secondo piano).
  2. FileDescriptor.sync() si occupa della sincronizzazione dei metadati? Questo è la sincronizzazione della directory del file modificato. Confronta con FileChannel.force().
  3. Di solito, uno non scrive direttamente in FileOutputStream. Ecco la mia soluzione (sei d'accordo?):
     
    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(); 
    } 
    

risposta

10

Android farà la sincronizzazione quando è necessario - come quando lo schermo si spegne, lo spegnimento del dispositivo, ecc Se si sta solo guardando operazione "normale", la sincronizzazione esplicita da parte delle applicazioni non è mai necessaria.

Il problema si presenta quando l'utente estrae la batteria dal proprio dispositivo (o esegue un hard reset del kernel) e si desidera assicurarsi di non perdere alcun dato.

Quindi, la prima cosa da realizzare: il problema è quando l'alimentazione viene improvvisamente persa, quindi non può verificarsi uno spegnimento pulito e la domanda su cosa accadrà nella memoria permanente a quel punto.

Se stai solo scrivendo un singolo file indipendente, non importa ciò che fai. L'utente potrebbe aver tirato la batteria mentre stavi scrivendo, proprio prima di iniziare a scrivere, ecc. Se non esegui la sincronizzazione, significa solo che c'è più tempo da quando hai finito di scrivere durante il quale tirare la batteria perderà i dati.

La grande preoccupazione qui è quando si desidera aggiornare un file. In tal caso, la prossima volta che leggete il file che si desidera avere sia le precedenti contenuti, o le nuove contenuti. Non vuoi ottenere qualcosa scritto a metà, o perdere i dati.

Questo è spesso fatto scrivendo i dati in un nuovo file e quindi passando a quello dal vecchio file. Prima di ext4 sapevi che, una volta finito di scrivere un file, ulteriori operazioni su altri file non sarebbero andate su disco fino a quelle su quel file, quindi potresti tranquillamente cancellare il file precedente o altrimenti fare operazioni che dipendono dal tuo nuovo file essendo completamente scritto.

Tuttavia ora se si scrive il nuovo file, quindi si elimina quello vecchio, e la batteria viene tirata, al prossimo avvio si può vedere che il vecchio file viene eliminato e il nuovo file creato ma il contenuto del nuovo file è non completo.Effettuando la sincronizzazione, ci si assicura che il nuovo file sia completamente scritto in quel punto, in modo da poter apportare ulteriori modifiche (come eliminare il vecchio file) che dipendono da tale stato.

+0

Mi sarei aspettato che flush() assicurasse che tutto sia scritto sul disco - essenzialmente chiamando sync(). Non è così? –

+0

@a_horse_with_no_name: No, non lo è. _flush_ e _sync_ sono due operazioni diverse: flush esegue il flush dei buffer intermedi; la sincronizzazione in realtà scrive nello spazio di archiviazione. Vedi per es. http://stackoverflow.com/questions/2340610/difference-between-fflush-and-fsync – sleske

+0

@sleske: grazie per il chiarimento! –

1

fileOut.getFD().sync(); deve essere nella clausola finally, prima dello close().

sync() è molto più importante di close() considerando la durata.

Quindi, ogni volta che si desidera "terminare" di lavorare su un file, è necessario sync() prima del close().

posix non garantisce che le scritture in sospeso verranno scritte su disco quando si emette un close().