Discussione:
File di testo che si corrompono
(troppo vecchio per rispondere)
z***@email.it
2013-11-20 16:17:38 UTC
Permalink
salve,
ho un problema un pò particolare. Ho realizzato un'applicazione win32 (scritta in c++) che gira su un pc embedded con windows XP embedded, la cui unica particolarità che ci interessa è il files system readonly, in modo che questo pc embedded possa essere spento in qualunque momento semplicemente togliendo corrente (quindi senza fare la procedura di shotdown).

Tutto perfetto. Però ho il problema di dover scrivere un log.
Quindi ho fatto 2 partizioni: una con il sistema operativo che è readonly, mentre l'altra è libera. Su questa porzione libera viene scritto il log, che TEORICAMENTE viene scritto solo durante la lavorazione e MAI NESSUNO SPEGNE IMPROVVISAMENTE la macchina DURANTE LA LAVORAZIONE!

Eppure ogni tanto viene trovato questo maledetto file corrotto.
NOTARE BENE: questa partizione libera viene usata anche da altre applicazioni che girano e che scrivono, ma soltanto il mio log è soggetto a questo difetto.
A dire il vero nemmeno scommetto che sia davvero causato da uno spegnimento improvviso! Ma alcuni dettagli m'hanno fatto sospettare di questo e vorrei indagare (almeno per poter eventualmente escluderlo).

Il mio sospetto è che ,nonostante io usi la open(), write() e SUBITO la
close() (non lo lascio mai aperto ad oltranza), windows non scriva immediatamente. Nonostante la close().
Altro sub-sospetto è che ciò avvenga a causa dei parametri che uso io nella
open(). Parametri che però ho modificato spesso, durante i miei tentativi di risoluzione.

Altro dettaglio: quando il file si corrompe, lo trovo apparentemente vuoto...e invece è pieno di 0 binari (null), in quantità più o meno uguale a quella che dovrebbe essere se il contenuto fosse giusto.

La open la faccio cosi':
int fdStatistiche = open("nomefile", O_RDWR | O_TRUNC | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);

e ogni volta riscrivo l'intero contenuto (per questo c'è la O_TRUNC e O_CREATE), con una normale write(), di cui controllo il valore di ritorno che deve essere uguale alla lunghezza del byte scritti (e questo errore non è mai accaduto).
Immediatamente dopo: close(fdStatistiche);


Per funzionare, funziona parfettamente. Ma ogni tanto qualche cliente chiama perche' il file è corrotto.
Qui da me è successo solo 1 volta, e guardacaso dopo che era saltata la corrente per un temporale.
Ovvamente buttando giù all'improvviso volutamente, non ricapita più!

Buttate idee, grazie! :)
acc
2013-11-20 17:36:59 UTC
Permalink
Post by z***@email.it
Il mio sospetto è che ,nonostante io usi la open(), write() e SUBITO la
close() (non lo lascio mai aperto ad oltranza), windows non scriva immediatamente. Nonostante la close().
E' cosi', puoi ovviare utilizzando la CreateFile() che ti permette di
bypassare la cache, usando il flag FILE_FLAG_WRITE_THROUGH, vedi qua:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx#caching_behavior

Oppure usare la funzione FlushFileBuffers(), per forzare lo scarico dei
dati sul disco. In questo caso non occorre impostare alcun flag in apertura.

Se si tratta di un file che resta aperto a lungo, dove ogni tanto scrivi
qualcosa, la prima soluzione e' piu' comoda, magari utilizzando un
buffer multiplo di 4096, per evitare un'eccessiva occupazione di CPU.
Se invece scrivi tutto in una botta sola, conviene la seconda.
Post by z***@email.it
Altro dettaglio: quando il file si corrompe, lo trovo apparentemente vuoto...e invece è pieno di 0 binari (null), in quantità più o meno uguale a quella che dovrebbe essere se il contenuto fosse giusto.
int fdStatistiche = open("nomefile", O_RDWR | O_TRUNC | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
e ogni volta riscrivo l'intero contenuto (per questo c'è la O_TRUNC e O_CREATE), con una normale write(), di cui controllo il valore di ritorno che deve essere uguale alla lunghezza del byte scritti (e questo errore non è mai accaduto).
Immediatamente dopo: close(fdStatistiche);
Questo e' male, ti consiglio piuttosto di scrivere su di un nuovo file,
magari usando la GetTempFileName() per avere un nome unico e,
successivamente, cancellare il vecchio file e rinominare il nuovo.

Il valore di ritorno della write() e' poco affidabile, ti dice solo che
i byte sono stati scritti nella cache di sistema, ma non e' detto che
siano stati effettivamente "scaricati" sul disco.
z***@email.it
2013-11-21 08:07:44 UTC
Permalink
Post by acc
E' cosi', puoi ovviare utilizzando la CreateFile() che ti permette di
[...]
Post by acc
ti consiglio piuttosto di scrivere su di un nuovo file,
Oh! Interessante!
Prima di tutto proverò a usare le API (CreateFile, ecc) con le giuste flag per non bufferizzare, anzichè le classiche funzioni open, write, close.

Poi mi hai fatto anche venire in mente un'altra questione: il disco è in realtà una flash e quindi ha il problema che ,riscrivendo sempre la stessa area fisica, si consuma.
Anche per questo è meglio che faccia cosi': rinomino Log in Log_old, creo un nuovo file Log e cancello successivamente Log_old (quindi senza usare la MoveFile()).
Tra l'altro, se qualcosa si interrompe a metà, ho ancora Log_old dove i dati non sono aggiornati ma è meglio che niente.

Bene! Che bello aver qualcuno con cui poterne parlare!

Continua a leggere su narkive:
Loading...