出自:http://hi.baidu.com/widebright/item/f2e98bcb64239e46a9ba948e
都是为了减少用户和内核直接的数据复制的, 就是所谓的 “zero copy”。
普通的 read 和write系统调用,数据需要从内核空间复制到用户空间的。
sendfile
http://linux.die.net/man/2/sendfile
http://lxr.linux.no/linux+v3.5.4/fs/read_write.c#L1000
在两个文件描述符之间传输数据,不用拷贝。 但 输入的描述符必须是真正的文件, 输出的文件描述符可以是 socket。 这也是sendfile的由来吧。他是从文件的缓存页 page cache里面直接把数据传输到另外一个描述符里面去,省去用户空间和内核空间的复制。 看当前代码他是使用一个专门的do_splice_direct 函数来实现的。 思路跟 splice是一样的,也需要使用pipe来做中介,但他这个do_splice_direct 使用一个每个进程缓存(在 corrent指针的 splice_pipe)的一个pipe,可以少用一次系统调用(正常的splice需要从 文件到 pipe,然后再从pipe到socket,有两次调用)。
这个sendfile应用场合,比如像http服务器,直接把htm源文件读出来发送给客户可对应的socket时,用这个sendfile就很合适。减少数据复制的同时,应该 系统调用的次数也减少了。 看网上共识,这种应用环境使用sendfile可以提到性能是不争的事实。
splice
http://linux.die.net/man/2/splice
http://lwn.net/Articles/119680/
http://yarchive.net/comp/linux/splice.html
http://lxr.linux.no/linux+v3.5.4/fs/splice.c
在两个文件描述符之间传输数据,不用拷贝。但输入和输出文件描述符必须有一个是pipe。也就是说如果你需要从一个socket 传输数据到另外一个socket,是需要使用 pipe来做为中介的。 pipe buffer被抽象出来,当作 “内核缓存结构”, 一种流缓冲,可以理解成你的数据从写入 “内核流缓存”里面,然后在从 一个”内核流缓存“复制到另外一个比如说socket的缓存。全部数据都是在内核空间进行。 当然你的数据复制也是不用复制,他那个pipe buffer本来就是 使用page去管理缓存的,就是 缓存地址加偏移地址的办法,只是Linus 觉splice的需要很像之前的pipe思想,所以splice就用这个个pipe来作为”内核缓存结构“了。
看起来splice是可以避免数据的复制,应该能获得更好的性能。但好像网上的评测,一些人使用了splice之后性能反而下降。其实大家最想要的还是从socket到socket的 ”零拷贝“技术,这样那些代理服务器啊什么的转发数据的时候就可以提高性能。但这个splice用到这个场合的话, 还是需要一个pipe来做中介的,要调用两个splice才能把数据从一个socket移到另外一个socket。
splice (socket1_fd, pipe_fd
splice (pipl_fd, socket2_fd
这样,系统调用同样需要两次。read write也需要两次。系统调用次数没有减少,不像sendfile那样可以减少一个系统调用。 数据复制的代价,可能数据包比较小时,影响应该是比较小的,所以 splice没有起到意想的作用。有人用性能工具比较了splice的时候的内核调用的区别,说只是性能消耗的地方转移了而已。这个需要那个高人来使用最新的内核测试分析一下了。
haproxy有配置使不使用splice的选项,splice的使用也可以去参考一下 haproxy的代码。