Modifica dei file senza condizioni di gara?

1

Ho un file CSV che deve essere modificato da più processi contemporaneamente. La mia domanda è: come posso fare questo senza introdurre condizioni di gara?

È facile scrivere alla fine del file senza condizioni di gara aprendo (2) in modalità "a" (O_APPEND) e scrivendo semplicemente su di esso. Le cose diventano più difficili quando si rimuovono le righe dal file.

La soluzione più semplice è leggere il file in memoria, apportare modifiche e sovrascriverlo al file. Se un altro processo lo scrive dopo che è in memoria, tuttavia, i nuovi dati andranno persi dopo la sovrascrittura. Per complicare ulteriormente le cose, la mia piattaforma non supporta i blocchi di record POSIX, il controllo della presenza di file è una condizione di competizione in attesa di accadere, rinominare (2) sostituisce il file di destinazione se esiste invece di fallire e la modifica dei file sul posto lascia byte vuoti in esso a meno che i rimanenti byte non vengano spostati all'inizio del file.

La mia idea di rimuovere una linea è questa (in pseudocodice):

    filename = "/home/user/somefile";
    file = open(filename, "r");
    tmp = open(filename+".tmp", "ax") || die("could not create tmp file"); //"a" is O_APPEND, "x" is O_EXCL|O_CREAT
    while(write(tmp, read(file)); //copy the $file to $file+".new"
    close(file);
//edit tmp file
    unlink(filename) || die("could not unlink file");
    file = open(filename, "wx") || die("another process must have written to the file after we copied it."); //"w" is overwrite, "x" is force file creation
    while(write(file, read(tmp))); //copy ".tmp" back to the original file
    unlink(filename+".tmp") || die("could not unlink tmp file");

O starei meglio con un semplice file di blocco? Processo di appender:

lock = open(filename+".lock", "wx") || die("could not lock file");
file = open(filename, "a");
write(file, "stuff");
close(file);
close(lock);
unlink(filename+".lock");

Processo editor:

lock = open(filename+".lock", "wx") || die("could not lock file");
file = open(filename, "rw");
while(contents += read(file));
//edit "contents"
write(file, contents);
close(file);
close(lock);
unlink(filename+".lock");

Entrambi si basano su un file aggiuntivo che verrà lasciato in sospeso se un processo termina prima di scollegarlo, facendo sì che altri processi rifiutino di scrivere sul file originale.

A mio parere, questi problemi sono causati dal fatto che il sistema operativo consente di aprire più descrittori di file scrivibili sullo stesso file nello stesso momento, invece di fallire se un descrittore di file scrivibile è già aperto. Sembra che O_CREAT | O_EXCL sia la cosa più vicina a una soluzione reale per prevenire le condizioni di gara del filesystem, a parte i blocchi dei record POSIX.

Un'altra possibile soluzione è quella di separare il file in più file e directory, in modo da ottenere un controllo più granulare su componenti (linee, campi) del file usando O_CREAT | O_EXCL. Ad esempio, "file / $ id / $ campo" conterrà il valore della colonna $ campo della riga $ id. Non sarebbe più un file CSV, ma potrebbe funzionare.

Sì, so che dovrei usare un database per questo dato che i database sono costruiti per gestire questi tipi di problemi, ma il programma è relativamente semplice e speravo di evitare l'overhead.

Quindi, uno di questi modelli funzionerebbe? Esiste un modo migliore? Qualsiasi comprensione di questo tipo di problemi sarebbe apprezzata.

    
posta user2569445 31.10.2013 - 15:30
fonte

1 risposta

9

Non reinventare la ruota. Lo avrai sbagliato in modi sottili e frustranti.

Utilizza un database low-overhead appropriato come sqlite (come indicato da Zoredache o Chopper3) creato da persone che hanno già fatto tutto il lavoro per te.

    
risposta data 31.10.2013 - 17:26
fonte

Leggi altre domande sui tag