splice(2) considered useful?

无意中发现Linux还有splice(2)这么个玩意儿,而且Linus本人对其很是赞赏,还一门心思地给它做广告……那我们就来看看它到底是何方神圣。

splice(2)绝对比我想象中要难以理解,一开始以为它的功能和dup2(2)差不多,然后觉得不对,似乎和sendfile(2)差不多,后来发现也不对!(否则还加这么一个系统调用干嘛?!)它和sendfile(2)的差别还算是比较大的,后面我们会细谈。

splice(2)这东西,其实它的名字就已经告诉我们它是做什么的了,可因为它牵扯到了pipe,所以就变得不好理解了。我们知道,Unix里的pipe有两端,一个写另一个读。我们通常都用pipe来实现进程间的fifo通信。而splice却用pipe来连接一些东西,所以,它名字就可以告诉我们,它是用来把其它fd和pipe的某一端拼接到一起的!也就是说,splice(2)的两个fd参数中必须有一个是pipe的一端,否则就会产生EINVAL。而且,当某个fd是pipe一端时,其对应的offset pointer必须是NULL。而且而且,splice(2)不会block,读不到数据时它会返回0。还有一点格外重要,一定要注意数据流动的方向,splice(2)对这个方向是很敏感的。

我们可以看出,splice(2)和dup2(2)语意差别显著,并且,当调用两次splice(2)把pipe两端连接到两个其它文件时,其功能就等价于sendfile(2),都是zero copy。进一步思考,其实splice(2)控制的是某个不可见的buffer,而dup2(2)和sendfile(2)控制的都是文件本身。

wikipedia上也有对splice(2)的解释),不怎么详细,里面给的例子还可以,我对着它做了改进,算是用splice(2)实现了sendfile(2)的功能,代码见下。

注意1:LWN上的这篇文章过时了,不用看了。

注意2:man pages里对splice(2)原型的描述是错的!那两个表示offset的参数的类型都应该是loff_t,而不是off_t!我已经做了一个补丁提交给man pages的维护者。

补丁:http://wangcong.org/down/man-2-splice-loff_t.diff

代码:http://wangcong.org/down/splice_copy.c