19

sendfile() 在内核空间内的两个文件描述符之间复制数据。我在某个地方看到如果你在 linux 中用 C 语言编写 Web 服务器,你应该使用 send() 和 recv() 而不是 write() 和 read()。那么 send() 是否也使用内核空间?

无论我用于发送什么 - sendfile() 或 send() - 在客户端我都会使用 recv() 对吗?

另一方面,手册页说:“send() 和 write(2) 之间的唯一区别是标志的存在。使用零标志参数,send() 等效于 write(2)。”

4

3 回答 3

38

如果fd是一个套接字文件描述符,那么这些系统调用是相同的:

  • send(fd, data, length, 0)是相同的write(fd, data, length)
  • recv(fd, data, length, 0)是相同的read(fd, data, length)

因此,除非您需要设置非零flags参数,否则使用send/recv或没有区别write/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);
于 2012-11-04T03:09:05.057 回答
3

正如您所指出的,唯一的区别是标志。send/recv 用于网络,而 read/write 是任何文件描述符的通用 I/O 函数。send 仅在您想使用标志时才比 write 有用,因为标志都与网络相关,因此在非网络文件描述符上调用 send 没有意义(我也不确定它是否有效)。

另外你应该注意:

in_fd 参数必须对应于支持类似 mmap(2) 操作的文件(即,它不能是套接字)。

这意味着您不能从套接字复制(您可以复制到套接字,在 2.6.33 之前,您必须复制到套接字)。

于 2012-11-04T03:07:10.887 回答
1

sendPOSIX 标准指定,它说:

send() 函数等效于带有空指针 dest_len 参数的 sendto(),如果不使用标志,则等效于 write()。

sendfile是特定于 Linux 的。它告诉内核执行从文件到套接字的零拷贝 I/O。(请注意,它仅在源 fd 是文件而目标是套接字时才有效;对于通用 Linux 特定的零拷贝 I/O,请阅读有关内容splice()。)

请注意,很少需要使用特定于 Linux 的零拷贝 I/O。带有小用户空间缓冲区 (8K-16K)的标准且可移植的read+ write(or send) 循环通常会将该缓冲区保留在 L1 缓存中,从系统 RAM 的角度来看,它相当于“零复制”。

因此,除非您的分析显示您的特定应用程序有所不同,否则请坚持使用标准接口。只是MHO。

于 2012-11-04T03:11:38.060 回答