当引入splice时,在内核列表上讨论了sendfile是基于splice重新实现的。接头SLICE_F_MOVE的文档指出:
那么,这是否意味着Linux没有用于写入套接字的零复制方法?还是在某个时候解决了这个问题,并且多年来没有人更新文档?在最新的3.x内核版本中,sendfile或splice都具有零拷贝实现吗?
由于Google对此查询没有答案,因此我为下一个可怜的schmuck创建了一个stackoverflow问题,他想知道使用vmsplice和splice或sendfile是否比普通的旧写入有任何好处。
最佳答案
sendfile
一直存在,并且仍然是零拷贝(假设硬件允许这样做,但是通常是这种情况)。零复制是首先进行此系统调用的全部要点。如今,sendfile
被实现为splice
的包装器。
这表明splice
也是零复制的,的确如此。至少在理论上,至少在某些情况下。问题是弄清楚如何正确使用它,以便可靠地工作,因此它是零拷贝。至少可以说,该文档是稀疏的。
特别是,如果页面被指定为“礼物”,那么splice
仅能将副本复制为零,也就是说,您不再拥有这些页面(从形式上讲,但实际上您仍然拥有)。如果仅将文件描述符拼接到套接字上就没有问题,但是如果要从应用程序的地址空间或从一个管道到另一个管道拼接数据,则是一个大问题。目前尚不清楚以后(以及何时)要处理这些页面。文档指出,您以后不得触摸页面或对其进行任何操作,永远不要,永远不要。因此,如果您遵循文档中的字母,则必须泄漏内存。
显然这是不正确的(不可能),但是没有(好的,至少对您来说)知道何时可以安全地重用或释放该内存的好方法。执行sendfile
的内核将知道,因为一旦它收到TCP ACK,便知道不再需要该数据。问题是,您永远不会看到ACK。当splice
返回时,您所知道的只是数据已被接受发送(但您不知道它是否已经发送或接收,也不知道何时发生)。
这意味着您需要以某种方式在应用程序层上解决此问题,方法是执行手动ACK(使用可靠的UDP免费提供),或者假设如果对方发送了您的请求的答案,则显然他们必须已经获得了该请求。 。
您必须管理的另一件事是有限的管道空间。默认值很小,但是即使您增加了大小,也不能天真地拼接任何大小的文件。另一方面,sendfile
会让您做到这一点,这很酷。
总而言之,sendfile
很不错,因为它可以正常工作,而且效果很好,并且您无需关心上述任何细节。它不是万能药,但肯定是一个很好的补充。
我个人将远离splice
及其家族,直到对整个事情进行了彻底的检查,直到100%明确了您必须做的事情(以及何时要做)以及您不需要做的事情。
无论如何,对于普通的旧write
而言,真正有效的 yield 是微不足道的。我回想起几年前Torvalds先生的一些不那么客气的评论(当时BSD具有某种形式的write
,它可以对重新映射页面进行一些魔术操作以实现零拷贝,而Linux则没有),该评论指出进行复制通常是没有问题的,但是在页面上玩花样是[这里不再赘述]。
关于c - Linux是否有零拷贝?拼接还是sendfile?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24254098/