GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Die effizienteste Methode zum Kopieren einer Datei unter Linux

Wenn Sie wissen, dass sie Linux> 2.6.17, splice() verwenden werden ist der Weg, um unter Linux Zero-Copy zu machen:

 //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);

Leider können Sie sendfile() nicht verwenden hier, weil das Ziel kein Socket ist. (Der Name sendfile() kommt von send() + "Datei").

Für Zero-Copy können Sie splice() verwenden wie von @Dave vorgeschlagen. (Außer, es wird keine Nullkopie sein; es wird "eine Kopie" vom Seitencache der Quelldatei zum Seitencache der Zieldatei sein.)

Allerdings... (a) splice() ist Linux-spezifisch; und (b) Sie können mit ziemlicher Sicherheit genauso gut tragbare Schnittstellen verwenden, vorausgesetzt, Sie verwenden sie richtig.

Kurz gesagt, verwenden Sie open() + read() + write() mit einem kleinen temporärer Puffer. Ich schlage 8K vor. Ihr Code würde also etwa so aussehen:

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);
}

Mit dieser Schleife kopieren Sie 8 KB aus dem in_fd-Seitencache in den CPU-L1-Cache und schreiben sie dann aus dem L1-Cache in den out_fd-Seitencache. Dann überschreiben Sie diesen Teil des L1-Cache mit dem nächsten 8-KByte-Block aus der Datei und so weiter. Das Nettoergebnis ist, dass die Daten in buf wird überhaupt nie im Hauptspeicher gespeichert (außer vielleicht einmal am Ende); Aus Sicht des System-RAM ist dies genauso gut wie die Verwendung von "Zero-Copy" splice() . Außerdem ist es perfekt auf jedes POSIX-System portierbar.

Beachten Sie, dass der kleine Puffer hier entscheidend ist. Typische moderne CPUs haben etwa 32 KB für den L1-Datencache. Wenn Sie also den Puffer zu groß machen, wird dieser Ansatz langsamer. Möglicherweise viel, viel langsamer. Halten Sie den Puffer also im Bereich von "wenigen Kilobytes".

Natürlich ist die Speicherbandbreite wahrscheinlich nicht Ihr begrenzender Faktor, es sei denn, Ihr Festplattensubsystem ist sehr, sehr schnell. Daher würde ich posix_fadvise empfehlen um dem Kernel mitzuteilen, was Sie vorhaben:

posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);

Dies gibt dem Linux-Kernel einen Hinweis darauf, dass seine Read-Ahead-Maschinerie sehr aggressiv sein sollte.

Ich würde auch vorschlagen, posix_fallocate zu verwenden um den Speicher für die Zieldatei vorab zuzuweisen. Dies wird Ihnen im Voraus sagen, ob Ihnen die Festplatte ausgehen wird. Und für einen modernen Kernel mit einem modernen Dateisystem (wie XFS) hilft es, die Fragmentierung in der Zieldatei zu reduzieren.

Das Letzte, was ich empfehlen würde, ist mmap . Dank TLB-Thrashing ist es normalerweise der langsamste Ansatz von allen. (Sehr aktuelle Kernel mit "transparenten Hugepages" könnten dies abmildern; ich habe es in letzter Zeit nicht versucht. Aber es war sicherlich früher sehr schlecht. Also würde ich mir nur die Mühe machen, mmap zu testen wenn Sie viel Zeit zum Benchmarken und einen sehr aktuellen Kernel haben.)

[Aktualisieren]

In den Kommentaren wird gefragt, ob splice von einer Datei zur anderen ist Zero-Copy. Die Linux-Kernel-Entwickler nennen das „Page-Stealing“. Sowohl die Manpage für splice und die Kommentare in der Kernel-Quelle sagen, dass SPLICE_F_MOVE Flag sollte diese Funktionalität bereitstellen.

Leider ist die Unterstützung für SPLICE_F_MOVE wurde in 2.6.21 (damals 2007) entfernt und nie ersetzt. (Die Kommentare in den Kernelquellen wurden nie aktualisiert.) Wenn Sie die Kernelquellen durchsuchen, finden Sie SPLICE_F_MOVE wird eigentlich nirgends verwiesen. Die letzte Nachricht, die ich finden kann (von 2008), besagt, dass sie auf einen Ersatz wartet.

Die Quintessenz ist, dass splice von einer Datei zur anderen ruft memcpy auf um die Daten zu verschieben; es ist nicht Nullkopie. Das ist nicht viel besser als im Userspace mit read /write mit kleinen Puffern, also können Sie genauso gut bei den standardmäßigen, portablen Schnittstellen bleiben.

Wenn "Seitendiebstahl" jemals wieder zum Linux-Kernel hinzugefügt wird, dann die Vorteile von splice wäre viel größer. (Und selbst heute, wenn das Ziel ein Socket ist, erhalten Sie eine echte Zero-Copy, wodurch splice attraktiver.) Aber für den Zweck dieser Frage, splice kauft dir nicht viel.


Linux
  1. Kopieren Sie Dateien im Linux-Terminal

  2. Eine einfache Möglichkeit, Dateien und Verzeichnisse in Linux zu verstecken

  3. gcp – Ein erweitertes Dienstprogramm zum Kopieren von Dateien für Linux

  4. Installieren Sie AzCopy unter Linux – die schnellste Methode zum Kopieren in Azure

  5. Holen Sie sich die neueste Datei in einem Verzeichnis unter Linux

So kopieren Sie eine Datei in mehrere Verzeichnisse unter Linux

So kopieren Sie Dateien und Verzeichnisse unter Linux

Cp-Befehl in Linux (Dateien kopieren)

Kopieren Sie eine Datei in mehrere Verzeichnisse von der Befehlszeile unter Linux

So kopieren Sie Dateien und Verzeichnisse im Linux-Terminal

Linux-Kopierdatei - Linux-CP leicht gemacht!