欢迎光临散文网 会员登陆 & 注册

一文读懂零拷贝技术|splice使用

2022-10-15 16:03 作者:补给站Linux内核  | 我要投稿

服务端要向客户端连接发送一个文件,一般过程如下:

  • 服务端首先调用 read() 函数读取文件内容。

  • 服务端通过调用 write()/send() 函数将文件内容发送给客户端连接。

上面过程如下图所示:


从上图可以看出,在发送文件的过程中,首先需要将文件页缓存(Page Cache)从内核态复制到用户态缓存中,然后再从用户态缓存复制到客户端的 Socket 缓冲区中。

其实在上面的过程中,复制文件数据到用户态缓存这个操作是多余的,我们完全可以直接把文件页缓存的数据复制到 Socket 缓冲区即可,这样就可以减少一次拷贝数据的操作。

为了实现这样的功能,内核提供了一个名为 splice() 的系统调用,使用 splice() 系统调用可以避免从内核态拷贝数据到用户态。

不需要将内核态的数据拷贝到用户态缓存的技术被称为:零拷贝技术。

下面我们将介绍 splice() 系统调用的原理和实现。


【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)  

splice 使用实例

如果服务端要发送文件给客户端,使用 read()/write() 方式来实现的话,代码如下所示:

上面代码的流程比较简单,如下:

  • 首先通过调用 stat() 系统调用获取文件的大小。

  • 然后通过调用 read() 系统调用读取文件内容。

  • 最后通过调用 write() 系统调用将文件内容发送给客户端连接。

从上面的代码可以看出,使用 read()/write() 方式发送文件给客户端,首先需要将文件内容读到用户态缓存中,然后才能发送给客户端连接。

然而,将文件内容读取到用户态缓存这个过程是多余的,我们看看怎么使用 splice() 系统调用来避免将文件内容拷贝到用户态缓存。

使用 splice() 发送文件时,需要创建一个管道作为中转,代码如下:

从上面代码可以看出,使用 splice() 发送文件时,我们并不需要将文件内容读取到用户态缓存中,但需要使用管道作为中转。

其实这里的管道只是作为一个通道,并不会产生数据拷贝的,如下图所示:


对比 read()/write() 版本的原理图,可以看出 splice() 版本省去了拷贝文件内容到用户态缓存这个步骤。

总结

本文主要介绍了使用 read()/write() 方式传输文件与使用 splice() 方式传输文件的原理,也提供了这两种方式的实例代码。

当然,从原理上看,使用 splice() 方式传输文件会比 read()/write() 方式性能要高。但如果真实测试这两种方式,会发现性能相差并不大。这是由于 splice() 方式虽然减少了数据拷贝过程,但是其处理逻辑比 read()/write() 方式更为复杂,所以性能提升并不理想,有兴趣的读者可以自己测试一下。

下一篇文章,我们将会介绍 splice() 的实现过程。


原文作者:Linux内核那些事



一文读懂零拷贝技术|splice使用的评论 (共 条)

分享到微博请遵守国家法律