GNU/Linux >> Linux Esercitazione >  >> Linux

Il modo più efficiente per copiare un file in Linux

Se sai che useranno un Linux> 2.6.17, splice() è il modo per eseguire la copia zero in Linux:

 //using some default parameters for clarity below. Don't do this in production.
 #define splice(a, b, c) splice(a, 0, b, 0, c, 0)
 int p[2];
 pipe(p);
 int out = open(OUTFILE, O_WRONLY);
 int in = open(INFILE, O_RDONLY)
 while(splice(p[0], out, splice(in, p[1], 4096))>0);

Sfortunatamente, non puoi usare sendfile() qui perché la destinazione non è un socket. (Il nome sendfile() deriva da send() + "file").

Per la copia zero, puoi usare splice() come suggerito da @Dave. (Tranne che non sarà una copia zero; sarà "una copia" dalla cache della pagina del file di origine alla cache della pagina del file di destinazione.)

Tuttavia... (a) splice() è specifico per Linux; e (b) puoi quasi certamente fare altrettanto bene usando le interfacce portatili, a condizione che tu le usi correttamente.

In breve, usa open() + read() + write() con un piccolo tampone temporaneo. Suggerisco 8K. Quindi il tuo codice sarebbe simile a questo:

int in_fd = open("source", O_RDONLY);
assert(in_fd >= 0);
int out_fd = open("dest", O_WRONLY);
assert(out_fd >= 0);
char buf[8192];

while (1) {
    ssize_t read_result = read(in_fd, &buf[0], sizeof(buf));
    if (!read_result) break;
    assert(read_result > 0);
    ssize_t write_result = write(out_fd, &buf[0], read_result);
    assert(write_result == read_result);
}

Con questo ciclo, copierai 8K dalla cache della pagina in_fd nella cache L1 della CPU, quindi li scriverai dalla cache L1 nella cache della pagina out_fd. Quindi sovrascriverai quella parte della cache L1 con il successivo blocco di 8K dal file e così via. Il risultato netto è che i dati in buf non verrà mai effettivamente memorizzato nella memoria principale (tranne forse una volta alla fine); dal punto di vista della RAM di sistema, questo è buono quanto usare "zero-copy" splice() . Inoltre è perfettamente portabile su qualsiasi sistema POSIX.

Si noti che il piccolo buffer è fondamentale qui. Le tipiche CPU moderne hanno circa 32K per la cache dei dati L1, quindi se rendi il buffer troppo grande, questo approccio sarà più lento. Forse molto, molto più lento. Quindi mantieni il buffer nell'intervallo "pochi kilobyte".

Ovviamente, a meno che il sottosistema del tuo disco non sia molto, molto veloce, la larghezza di banda della memoria probabilmente non è il tuo fattore limitante. Quindi consiglierei posix_fadvise per far sapere al kernel cosa stai facendo:

posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);

Questo darà un suggerimento al kernel Linux che il suo meccanismo di read-ahead dovrebbe essere molto aggressivo.

Suggerirei anche di utilizzare posix_fallocate per preallocare l'archiviazione per il file di destinazione. Questo ti dirà in anticipo se finirai il disco. E per un kernel moderno con un file system moderno (come XFS), aiuterà a ridurre la frammentazione nel file di destinazione.

L'ultima cosa che consiglierei è mmap . Di solito è l'approccio più lento di tutti grazie al thrashing di TLB. (I kernel molto recenti con "transparent hugepages" potrebbero mitigare questo problema; non l'ho provato di recente. Ma certamente era pessimo. Quindi mi preoccuperei solo di testare mmap se hai molto tempo per il benchmark e un kernel molto recente.)

[Aggiorna]

C'è qualche domanda nei commenti sul fatto che splice da un file all'altro è copia zero. Gli sviluppatori del kernel Linux chiamano questo "furto di pagine". Entrambe le pagine man per splice e i commenti nel sorgente del kernel dicono che il file SPLICE_F_MOVE flag dovrebbe fornire questa funzionalità.

Sfortunatamente, il supporto per SPLICE_F_MOVE è stato strappato in 2.6.21 (nel 2007) e mai sostituito. (I commenti nei sorgenti del kernel non sono mai stati aggiornati.) Se cerchi nei sorgenti del kernel, troverai SPLICE_F_MOVE in realtà non è referenziato da nessuna parte. L'ultimo messaggio che riesco a trovare (dal 2008) dice che è "in attesa di una sostituzione".

La conclusione è che splice da un file all'altro chiama memcpy spostare i dati; non copia zero. Questo non è molto meglio di quello che puoi fare nello spazio utente usando read /write con piccoli buffer, quindi potresti anche attenerti alle interfacce portatili standard.

Se il "furto di pagine" viene aggiunto nuovamente al kernel Linux, allora i vantaggi di splice sarebbe molto maggiore. (E anche oggi, quando la destinazione è un socket, ottieni una copia zero reale, facendo splice più attraente.) Ma ai fini di questa domanda, splice non ti compra molto.


Linux
  1. Copia i file nel terminale Linux

  2. Un modo semplice per nascondere file e directory in Linux

  3. Il metodo più efficiente per svuotare il contenuto di un file?

  4. il modo più veloce per convertire file delimitati da tabulazioni in csv in linux

  5. Copia file Linux con ETA?

Come copiare un file in più directory in Linux

Come copiare file e directory in Linux

Comando Cp in Linux (copia file)

Copia un file in più directory dalla riga di comando su Linux

Come copiare file e directory nel terminale Linux

File di copia Linux - CP Linux semplificato!