2009-10-16 13 views
7

Hier ist eine andere Frage über splice(). Ich hoffe, ich benutze es, um Dateien zu kopieren, und versuche, zwei Spleißanrufe zu verwenden, die durch eine Pipe verbunden sind, wie das Beispiel auf der Wikipedia-Seite von splice. Ich schrieb einen einfachen Testfall, der nur den ersten 32 K Bytes aus einer Datei zu lesen versucht und schreibt sie in einer anderen:Wie kann ich mit der Funktion splice() von Linux eine Datei in eine andere Datei kopieren?

#define _GNU_SOURCE 
#include <fcntl.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 

int main(int argc, char **argv) { 
    int pipefd[2]; 
    int result; 
    FILE *in_file; 
    FILE *out_file; 

    result = pipe(pipefd); 

    in_file = fopen(argv[1], "rb"); 
    out_file = fopen(argv[2], "wb"); 

    result = splice(fileno(in_file), 0, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE); 
    printf("%d\n", result); 

    result = splice(pipefd[0], NULL, fileno(out_file), 0, 32768, SPLICE_F_MORE | SPLICE_F_MOVE); 
    printf("%d\n", result); 

    if (result == -1) 
     printf("%d - %s\n", errno, strerror(errno)); 

    close(pipefd[0]); 
    close(pipefd[1]); 
    fclose(in_file); 
    fclose(out_file); 

    return 0; 
} 

Als ich dies ausführen, scheint die Eingabedatei korrekt gelesen zu werden, aber den zweiten Splice-Aufruf schlägt mit EINVAL fehl. Weiß jemand, was ich hier falsch mache?

Danke!

+0

Für jeden, der dies liest, sollte der zweite 'Spleiß'-Aufruf nur versuchen, die Anzahl der Bytes aus der Pipe zu lesen, wenn der erste 'Spleiß'-Aufruf zurückkommt. Auf heutigen Linux ist die Standard-Pipe-Größe '65535'. – Jite

Antwort

3

Welche Art von Dateisystem (n) kopieren Sie nach/von?

Ihr Beispiel läuft auf meinem System, wenn beide Dateien auf ext3 sind, aber fehlschlägt, wenn ich ein externes Laufwerk verwende (ich vergesse nicht, wenn es DOS oder NTFS ist). Ich vermute, dass sich eine oder beide Dateien in einem Dateisystem befinden, das von Spleiß nicht unterstützt wird.

+0

NTFS wäre sinnvoll - das wird über FUSE implementiert, und der eigentliche Dateisystemtreiber wird als Userspace-Prozess ausgeführt. Mit anderen Dateisystemen kann das Dateisystem direkt mit dem Seitencache umgehen. Es ist eine Schande, dass 'splice()' keinen sauberen automatischen Fallback zu einer Kopierschleife hat, obwohl ... – bdonlan

+0

Es ist NTFS, obwohl Spleiß auch unter DOS nicht zu funktionieren scheint. Einigen Sie sich auf den Fallback. Ich bin versucht, einige einfache Benchmarks zu machen, da es für den Test mit dem bloßen Auge beeindruckend scheint. – Duck

4

Vom splice manpage:

EINVAL Target file system doesn't support splicing; target file is 
      opened in append mode; neither of the descriptors refers to a 
      pipe; or offset given for non-seekable device. 

Wir wissen, eine der Deskriptoren ist ein Rohr, und die Datei ist nicht offen im Append-Modus. Wir wissen auch, dass kein Offset gegeben ist (0 entspricht NULL - wollten Sie einen Zeiger auf einen Nullpunkt-Offset übertragen?), Das ist also nicht das Problem. Daher unterstützt das von Ihnen verwendete Dateisystem nicht das Verbinden mit Dateien.

+0

Das war das Problem. Vielen Dank! Ich hätte die Manpage ganz durchlesen sollen und nicht bemerkt, dass der Splice Dateisystem-abhängig ist. In meinem Fall habe ich auf einen NFS-Filer kopiert. Ich versuche, den schnellsten Weg zu finden, viele Dateien (der Durchschnitt bei etwa 10 MB) von einem NFS-Dateisystem zu einem anderen zu kopieren. mmap-ing der Quelldatei und mit write() sind nicht spektakulär. Danke für die Zeiger! –

2

Die splice(2) system call ist zum Kopieren zwischen Dateien und Pipes und nicht zwischen Dateien, so dass es nicht zum Kopieren zwischen Dateien verwendet werden kann, wie von den anderen Antworten darauf hingewiesen wurde.

Ab Linux 4.5 ist jedoch ein neuer copy_file_range(2) system call verfügbar, der zwischen Dateien kopieren kann. Im Falle von NFS kann es sogar serverseitig kopieren.

Die verknüpfte Manpage enthält ein vollständiges Beispielprogramm.