web-dev-qa-db-ja.com

C、sendfile()とsend()の違いは?

sendfile()は、カーネル空間内の2つのファイル記述子間でデータをコピーします。 LinuxのCでWebサーバーを作成している場合は、write()とread()を使用する代わりに、send()とrecv()を使用する必要があります。では、send()はカーネルスペースも使用しますか?

クライアント側で送信に使用するもの(sendfile()またはsend())は何でも、recv()を使用しますか?

反対に、 man page は次のように述べています。「send()とwrite(2)の唯一の違いは、フラグの存在です。フラグ引数がゼロの場合、send()はwrite(2)と同等です。 )。」

18
samsamara

fdがソケットファイル記述子の場合、これらのシステムコールは同じです。

  • send(fd, data, length, 0)write(fd, data, length)と同じです
  • recv(fd, data, length, 0)read(fd, data, length)と同じです

したがって、ゼロ以外のflagsパラメータを設定する必要がない限り、send/recvwrite/readのどちらを使用しても違いはありません。

sendfileシステムコールは最適化です。ソケットsockfdと通常のファイルfilefdがあり、ファイルデータをソケットにコピーしたい場合(たとえば、ファイルを提供するWebサーバーの場合)、次のようになります。このように書いてください:

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

ただし、これは非効率的です。これには、カーネルがファイルデータをユーザースペースにコピーして(read呼び出しで)、同じデータをカーネルスペースにコピーして戻す(send呼び出しで)ことが含まれます。

sendfileシステムコールを使用すると、そのコピーをすべてスキップして、カーネルにファイルデータを直接読み取らせ、一挙にソケットに送信させることができます。

sendfile(sockfd, filefd, NULL, BUFSIZE);
33
Adam Rosenfield

ご指摘のとおり、唯一の違いはフラグです。 send/recvはネットワーキング用ですが、read/writeは任意のファイル記述子の一般的なI/O関数です。 sendは、フラグを使用する場合にのみ役立ちます。フラグはすべてネットワークに関連しているため、ネットワーク以外のファイル記述子でsendを呼び出すことは意味がありません(また、それが有効かどうかもわかりません)。

また、次の点にも注意してください。

In_fd引数は、mmap(2)のような操作をサポートするファイルに対応している必要があります(つまり、ソケットにすることはできません)。

つまり、ソケットからコピーすることはできません(ソケットにコピーでき、2.6.33より前はソケットにコピーする必要があります)。

3
CrazyCasta

sendPOSIX標準で指定 、つまり:

Send()関数は、nullポインターdest_len引数を持つsendto()と同等であり、フラグが使用されていない場合はwrite()と同等です。

sendfileはLinux固有です。これは、ファイルからソケットへのゼロコピーI/Oを実行するようにカーネルに指示します。 (ソースfdがファイルで、宛先がソケットの場合にのみ機能することに注意してください。一般的なLinux固有のゼロコピーI/Oについては、splice()についてお読みください。)

Linux固有のゼロコピーI/Oを使用する必要はほとんどないことに注意してください。小さなユーザースペースバッファ(8K-16K)を使用した標準のポータブルread + write(またはsend)ループは、通常、そのバッファをL1キャッシュに保持し、システムRAMの観点からの「ゼロコピー」。

したがって、プロファイリングで特定のアプリケーションの違いが示されない限り、標準のインターフェイスを使用してください。ただMHO。

1
Nemo