C, sendfile() und send() Unterschied?

Lesezeit: 4 Minuten

sendfile() kopiert Daten zwischen zwei Dateideskriptoren innerhalb des Kernelbereichs. Irgendwo habe ich gesehen, wenn Sie einen Webserver in C unter Linux schreiben, sollten Sie send() und recv() verwenden, anstatt write() und read() zu verwenden. Benutzt send() also auch den Kernelspace?

Was auch immer ich zum Senden verwende – sendfile() oder send() – auf der Clientseite verwende ich recv() richtig?

Auf der Kehrseite, Manpage sagt: “Der einzige Unterschied zwischen send() und write(2) ist das Vorhandensein von Flags. Mit einem Null-Flag-Argument ist send() äquivalent zu write(2).”

Wenn fd ein Socket-Dateideskriptor ist, dann sind diese Systemaufrufe identisch:

  • send(fd, data, length, 0) ist das gleiche wie write(fd, data, length)
  • recv(fd, data, length, 0) ist das gleiche wie read(fd, data, length)

Also, es sei denn, Sie müssen einen Wert ungleich Null setzen flags Parameter, es macht keinen Unterschied, ob Sie verwenden send/recv oder write/read.

Das sendfile Systemaufruf ist eine Optimierung. Wenn Sie eine Steckdose haben sockfd und eine normale Datei filefd und Sie einige Dateidaten in den Socket kopieren möchten (z. B. wenn Sie ein Webserver sind, der eine Datei bereitstellt), können Sie dies folgendermaßen schreiben:

// Error checking omitted for expository purposes
while(not done)
{
    char buffer[BUFSIZE];
    int n = read(filefd, buffer, BUFSIZE);
    send(sockfd, buffer, n, 0);
}

Dies ist jedoch ineffizient: Dazu kopiert der Kernel die Dateidaten in den Userspace (in der read aufrufen) und dann dieselben Daten zurück in den Kernel-Space (in die send Anruf).

Das sendfile Mit dem Systemaufruf können wir das gesamte Kopieren überspringen und den Kernel die Dateidaten direkt lesen und auf einen Schlag an den Socket senden:

sendfile(sockfd, filefd, NULL, BUFSIZE);

  • Auf der anderen Seite kann der Webbrowser (Client) den Inhalt also nur lesen. Rechts?

    – Samsamara

    4. November 2012 um 4:42 Uhr

  • @user601Lwillmywastedtimebe: Dies wirkt sich nur auf die Effizienz auf dem Server aus. Es ist immer noch eine TCP/IP-Verbindung – diese ist für den Client unsichtbar.

    – Klaas van Gend

    4. November 2012 um 8:00 Uhr

Benutzer-Avatar
CrazyCasta

Wie Sie bereits gesagt haben, besteht der einzige Unterschied in den Flaggen. send/recv dienen der Vernetzung, während read/write allgemeine E/A-Funktionen für beliebige Dateideskriptoren sind. send ist im Vergleich zu write nur nützlich, wenn Sie ein Flag verwenden möchten, da die Flags alle netzwerkbezogen sind, macht es keinen Sinn, send für einen Dateideskriptor außerhalb des Netzwerks aufzurufen (ich bin mir auch nicht sicher, ob es überhaupt gültig ist).

Außerdem sollten Sie beachten:

Das in_fd-Argument muss einer Datei entsprechen, die mmap(2)-ähnliche Operationen unterstützt (dh es darf kein Socket sein).

Das bedeutet, dass Sie nicht von einem Socket kopieren können (Sie können auf einen Socket kopieren und vor 2.6.33 müssen Sie auf einen Socket kopieren).

  • Sollte ich also sendfile() anstelle von send() verwenden, wenn ich einen Webserver schreibe? aber Sie sagten ‘send/recv sind für das Netzwerk’, nicht sendfile().

    – Samsamara

    4. November 2012 um 3:12 Uhr

  • Siehe Adams Antwort. Die Kurzversion der Antwort lautet jedoch, es kommt darauf an. Grundsätzlich sind send/recv nützlich, da Sie Flags verwenden können. Insbesondere in einem Webserver in Produktionsqualität für hohes Volumen sollten Sie nicht blockierende E / A verwenden, indem Sie das MSG_DONTWAIT-Flag verwenden (nicht sicher, ob es eine andere Möglichkeit als Flags gibt, dies zu tun).

    – CrazyCasta

    4. November 2012 um 3:15 Uhr

send ist vom POSIX-Standard spezifiziertwas sagt:

Die send()-Funktion ist äquivalent zu sendto() mit einem Nullzeiger dest_len-Argument und zu write(), wenn keine Flags verwendet werden.

sendfile ist Linux-spezifisch. Es weist den Kernel an, Zero-Copy-I/O von einer Datei zu einem Socket auszuführen. (Beachten Sie, dass es nur funktioniert, wenn der Quell-FD eine Datei und das Ziel ein Socket ist; für generische Linux-spezifische Zero-Copy-E/A lesen Sie mehr splice().)

Beachten Sie, dass es selten notwendig ist, Linux-spezifische Zero-Copy-E/A zu verwenden. Der Standard und tragbar read+write (oder send)-Schleife mit einem kleinen Userspace-Puffer (8K-16K) behält diesen Puffer im Allgemeinen im L1-Cache, wodurch er aus Sicht des System-RAMs einer “Nullkopie” entspricht.

Bleiben Sie also bei Standardschnittstellen, es sei denn, Ihre Profilerstellung zeigt einen Unterschied für Ihre spezielle Anwendung. Nur MHO.

  • Nun, Sie werden immer noch 2 residente Kopien derselben Sache im L1-Cache haben (es sei denn, der Kernel verwendet eine Funktion, die mir nicht bekannt ist, um dies zu verhindern), die andere Dinge aus dem L1-Cache verdrängen würde. Außerdem nimmt das Kopieren wahrscheinlich die Reihenfolge der Anzahl der zu kopierenden Bytes von Taktzyklen an (möglicherweise so etwas wie die Anzahl der zu kopierenden Bytes/4 oder /8).

    – CrazyCasta

    4. November 2012 um 3:17 Uhr

  • sendfile() ist auch unter OS X verfügbar, nicht nur unter Linux.

    – Paulpjmmchugh

    26. Mai 2014 um 0:06 Uhr

  • Der Postix-Standardlink geht anscheinend nirgendwo mehr hin.

    – Glücksdonald

    2. November 2018 um 15:50 Uhr


  • @luckydonald: Ich habe es einmal versucht und es ist fehlgeschlagen, dann habe ich es noch einmal versucht und es hat funktioniert … Vielleicht eine Art Infrastrukturproblem?

    – Nemo

    3. November 2018 um 18:15 Uhr

1215790cookie-checkC, sendfile() und send() Unterschied?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy