sendfile()在内核空间中的两个文件描述符之间复制数据。在某个地方,我看到如果你在Linux中用C编写Web服务器,你应该使用send()和recv()而不是使用write()和read()。那么send()也使用内核空间吗?
无论我用什么发送 - sendfile()或send() - 在客户端我都会使用recv()吗?
另一方面,man page说:“send()和write(2)之间的唯一区别是标志的存在。使用零标志参数,send()等同于write(2) “。
答案 0 :(得分:33)
如果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);
答案 1 :(得分:3)
正如你所指出的,唯一的区别是旗帜。 send / recv用于联网,而读/写是任何文件描述符的通用I / O函数。当你想要使用标志时,send仅对写入有用,因为标志都是网络相关的,在非网络文件描述符上调用send是没有意义的(我也不确定它是否有效)。 / p>
另外你应该注意:
in_fd参数必须对应于支持的文件 mmap(2)式操作(即,它不能是套接字)。
这意味着您无法从套接字复制(您可以复制到套接字,而在2.6.33之前必须复制到套接字)。
答案 2 :(得分:1)
send
是specified by the POSIX standard,其中包含:
send()函数等效于带有空指针的sendto() dest_len参数,如果没有使用标志,则写入()。
sendfile
是特定于Linux的。它告诉内核从文件到套接字执行零拷贝I / O. (请注意,它仅在源fd是文件且目标是套接字时才有效;对于通用Linux特定的零拷贝I / O,请阅读splice()
。)
请注意,很少需要使用特定于Linux的零拷贝I / O.具有小用户空间缓冲区(8K-16K)的标准和可移植read
+ write
(或send
)循环通常会将该缓冲区保留在L1缓存中,使其等效于“零 - 复制“从系统RAM的角度来看。
因此,除非您的分析显示您的特定应用程序的差异,否则请坚持使用标准接口。只是MHO。