五分钟了解什么是零拷贝

系统调用

了解零拷贝之前,你需要先了解什么是系统调用。以下以linux为例。

在linux中,系统分为内核空间和用户空间,我们所运行的JAVA等应用程序是运行在用户空间的,而用户空间是无法直接操作硬盘,网卡等硬件设备的。

因此需要通过系统调用来让内核帮我们读写硬件上的数据。

而所谓的系统调用,其实就是内核给我们提供的一些方法,例如read方法,你可以在linux中使用man 2 read 看到该方法的声明:
在这里插入图片描述

读一个文件时发生了什么

当我们用户空间的应用要读一个硬盘上的文件时,将发生以下步骤:

  1. 用户空间发起read系统调用,用户态转为内核态
  2. CPU利用DMA技术将硬盘数据拷贝到内核空间的读缓冲区
  3. CPU将内核读缓冲区的数据拷贝到用户缓冲区
  4. 系统调用结束,内核态切回用户态

示意图如下:
在这里插入图片描述
以上过程中发生了:2次态切换(上下文切换),1次DMA拷贝,1次CPU拷贝

传统I/O

如果是一次网络请求,应用程序读取一个文件并返回给远在它方的浏览器,那么又发生了什么?

  1. 执行上述的读操作,得到硬盘上的文件数据
  2. 执行一次写操作,将数据写入网卡,从而将数据返回给请求方

示意图如下:
在这里插入图片描述
以上过程中发生了:4次态切换(上下文切换),2次DMA拷贝,2次CPU拷贝

零拷贝

所谓零拷贝,就是为了减少上下文切换次数,减少拷贝次数,以达到提高性能的目的。

那么零拷贝是如何实现的呢?首先你要明白,零拷贝是内核实现的,而不是用户态应用程序实现的,用户态的进程只能通过系统调用来实现各种功能。

mmap零拷贝

mmap也是一种系统调用,可以通过man 2 mmap 来查看说明。

mmap能够开辟一块用户空间,并且与内核缓冲区做一个映射,简单来说就是开辟了一块“用户”与“内核”共享的一块空间。

那么就减少了一次CPU从内核空间到用户空间的数据拷贝
在这里插入图片描述
以上过程中发生了:4次态切换(上下文切换),2次DMA拷贝,1次CPU拷贝

从上图可以看到,mmap方案并不够理想,其实还是有一次拷贝,4次上下文切换。于是有了sendfile

sendfile实现零拷贝

linux 内核2.1版本引入了sendfile,可以使用man 2 sendfile 查看说明。

sendfile的方式是直接将数据从内核空间的“读缓冲区”拷贝到“socket缓冲区”,与mmap方式相比:

  1. mmap方式调用了read和write两次系统调用,sendfile只调用了sendfile
  2. 两者都有一次CPU拷贝
    示意图如下:
    在这里插入图片描述
    以上过程发生了:2次态切换(上下文切换),2次DMA拷贝,1次CPU拷贝

看到这里,大家一定想喷人了,说好的零拷贝呢?怎么搞来搞去还有一次拷贝。不要急,linux内核版本2.4优化了sendfile,实现了真正的零拷贝。

2.4版本所做的修改,就是将原本的CPU拷贝数据操作,改为:将读缓冲区的文件描述符添加到socket缓冲区中。

那么DMA在读取socket缓冲区时,其实是通过文件描述符信息直接读取到了数据,而数据本身并没有被拷贝,被拷贝的只是个“文件描述符”而已。

示意图如下:
在这里插入图片描述
以上过程发生了:2次态切换(上下文切换),2次DMA拷贝,0次CPU拷贝

Splice实现零拷贝

sendfile并不是零拷贝的唯一选择,splice是linux内核2.6.17引入,可以使用man 2 splice 查看说明

splice零拷贝的原理和sendfile差不多,它两的区别是:

  1. sendfile通过传递文件描述符来减少数据拷贝
  2. splice在两个缓冲区之间建立管道(pipe)来减少数据拷贝

在这里插入图片描述
以上过程发生了:2次态切换(上下文切换),2次DMA拷贝,0次CPU拷贝

总结

零拷贝方案对比:

系统调用上下文切换次数DMA拷贝次数CPU数据拷贝次数
mmap & write421
sendfile 2.1 版本221
sendfile 2.4 版本220
splice220
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值