当引入splice时,在内核列表中讨论了sendfile是基于splice重新实现的。 splice SLICE_F_MOVE的文档说明:
尝试移动页面而不是复制。这是 只提示内核:页面可能仍然存在 如果内核无法移动页面,则复制 管道,或管道缓冲器不参考 整页。最初的实现 flag是buggy:因此从Linux 2.6.21开始 这是一个无操作(但仍然允许在一个 splice()call);在将来,一个正确的 实施可能会恢复。
这是否意味着Linux没有写入套接字的零拷贝方法?或者这是在某些时候修复的,多年来没有人更新文档? sendfile或splice中的任何一个在最新的3.x内核版本中都有零复制实现吗?
由于Google对此查询没有答案,因此我为下一个想要了解使用vmsplice和splice或sendfile是否有利于普通旧写的穷人schmuck创建了一个stackoverflow问题。
答案 0 :(得分:15)
sendfile
从那时起一直是零拷贝(假设硬件允许它,但通常情况就是如此)。零拷贝是首先拥有这个系统调用的全部要点。 sendfile
现在已作为splice
的包装实现。
这表明splice
也是零拷贝,事实确实如此。至少在理论上,至少在某些情况下。问题是弄清楚如何正确使用它,以便它可靠地工作,因此它是零拷贝。至少可以说文档是......稀疏的。
特别是splice
只有在页面被提供为"礼物"时才能进行零拷贝,即你不再拥有它们(正式但实际上你还是做)。如果您只是将文件描述符拼接到套接字上,那么这是一个非问题,但如果您想要从应用程序的地址空间或从一个管道到另一个管道拼接数据,这是一个大问题。目前还不清楚如何处理页面(以及何时)。文档说明您之后可能不会触摸页面或对它们执行任何操作,从不,永远不会。因此,如果您按照文档的说明,则必须泄漏内存
这显然不正确(它不能),但是当重用或安全时,没有好的方法知道(至少对你来说!)释放那个记忆。执行sendfile
的内核会知道,因为一旦收到TCP ACK,它就知道再也不需要数据了。问题是,你永远不会看到一个ACK。您splice
返回的所有信息都是接受发送的数据(但您不知道它是否已经发送或接收,也不会发生这种情况)。<登记/>
这意味着您需要在应用程序层上解决某种方式,或者通过手动ACK(免费提供可靠的UDP),或者假设如果另一方发送您的请求的答案,他们显然必须得到这个要求。
您必须管理的另一件事是有限的管道空间。默认值非常小,但即使您增加了大小,也不能天真地拼接任何大小的文件。另一方面,sendfile
会让你这样做,这很酷。
总而言之,sendfile
很好,因为它只是有效,而且很好,你不需要关心上述任何细节。它不是灵丹妙药,但肯定是一个很好的补充
我个人会远离splice
及其家人,直到整个事情被彻底检修,直到100%明确你必须做什么(以及何时)以及你不必做什么
无论如何,对于大多数应用来说,相对于普通的write
而言,实际有效的收益是微不足道的。我记得几年前Torvalds先生发表的一些不太礼貌的评论(当BSD有一种write
形式,可以通过重新映射页面获得零拷贝,而Linux没有做到这一点)指出制作副本通常不是任何问题,但玩网页的技巧是 [不会在这里重复] 。
答案 1 :(得分:3)
根据2014-07-08关于拼接的相关手册页,我引用:
虽然我们谈到复制,但通常避免使用实际的副本。内核通过将管道缓冲区实现为一组指向内核内存页面的引用计数指针来实现此目的。内核创建&#34;副本&#34;缓冲区中的页面,通过创建指向页面的新指针(用于输出缓冲区),并增加页面的引用计数:仅复制指针,而不复制缓冲区的页面。
因此,是的,在大多数情况下,拼接记录为当前零拷贝。