零拷贝技术! 一张图带你看懂 IO

01、背景引见

置信不少的网友,在很多的博客文章外面,曾经见到过零拷贝这个词,会不由的收回一些不懂,什么是零拷贝?

从字面上咱们很容易了解出,零拷贝蕴含两个意思:

合起来了解,零拷贝就是不须要将数据从一个存储区域复制到另一个存储区域。

果然是这样的吗?

在 Linux 2.4 内核中,sendfile 系统调用方法,可以将磁盘数据经过 DMA 拷贝到内核态 Buffer 后,再经过 DMA 拷贝到 NIC Buffer(socket buffer),无需 CPU 拷贝,这个环节被称之为零拷贝。

从这段形容外面咱们可以得悉,站在操作系统的角度,零拷贝没有说不须要拷贝数据,而是省掉了 CPU 拷贝环节,缩小了不用要的拷贝次数,优化数据拷贝效率。

要想深度的了解这外面的原理,咱们还得从 IO 拷贝机制说起!

02、IO 拷贝机制引见

2.1、传统数据拷贝流程

以客户端从主机下载文件为例,相熟服务端开发的同窗或者知道,服务端须要做两件事:

理想上看似便捷的操作,外面的流程却没那么便捷,例如运行程序从磁盘中读取文件内容的操作,大体会经过以下几个流程:

整个读取数据的环节,成功了 1 次 DMA 拷贝,1 次 CPU 拷贝,2 次 CPU 切换;反之写入数据的环节,也是一样的。

整个拷贝环节,可以用如下流程图来形容!

从上图,咱们可以得出如下论断,4 次拷贝次数、4 次高低文切换次数。

而实践 IO 读写,有时刻须要启动 IO 终止,同时也须要 CPU 照应终止,拷贝次数和切换次数比预期的还要多,以致于当客户端启动资源文件下载的时刻,传输速度总是不尽人意。

那有没有好的方法来优化资源拷贝的速度呢?

答案是必需的,传统的数据拷贝流程还有很大的优化空间。

上方咱们一同来看看几种其它的拷贝形式。

2.2、mmap 内存映射拷贝流程

mmap 内存映射的拷贝,指的是将用户运行程序的缓冲区和操作系统的内核缓冲区启动映射解决,数据在内核缓冲区和用户缓冲区之间的 CPU 拷贝将其省略,进而放慢资源拷贝效率。

整个拷贝环节,可以用如下流程图来形容!

mmap 内存映射拷贝流程,从上图可以得出如下论断:

整个环节省掉了数据在内核缓冲区和用户缓冲区之间的 CPU 拷贝环节,在实践的运行中,对资源的拷贝能优化不少。

2.3、Linux 系统 sendfile 拷贝流程

在 Linux 2.1 内核版本中,引入了一个系统调用方法:sendfile。

当调用 sendfile() 时,DMA 将磁盘数据复制到内核缓冲区 kernel buffer;然后将内核中的 kernel buffer 间接拷贝到 socket buffer;最后应用 DMA 将 socket buffer 经过网卡传输给客户端。

整个拷贝环节,可以用如下流程图来形容!

Linux 系统 sendfile 拷贝流程,从上图可以得出如下论断:

相比 mmap 内存映射形式,Linux 2.1 内核版本中 sendfile 拷贝流程省掉了 2 次用户态和内核态的切换,同时内核缓冲区和用户缓冲区也无需建设内存映射,对资源的拷贝能优化不少。

2.4、sendfile With DMA scatter/gather 拷贝流程

在 Linux 2.4 内核版本中,对 sendfile 系统方法做了优化更新,引入 SG-DMA 技术,须要 DMA 控制器支持。

其实就是对 DMA 拷贝添加了 scatter/gather 操作,它可以间接从内核空间缓冲区中将数据读取到网卡。经常使用这个特点来成功数据拷贝,可以多省去一次性 CPU 拷贝。

整个拷贝环节,可以用如下流程图来形容!

Linux 系统 sendfile With DMA scatter/gather 拷贝流程,从上图可以得出如下论断:

可以发现,sendfile With DMA scatter/gather 成功的拷贝,其中 2 次数据拷贝都是 DMA 拷贝,全程都没有经过 CPU 来拷贝数据,一切的数据都是经过 DMA 来启动传输的,这就是操作系统真正意义上的零拷贝(Zero-copy) 技术,相比其余拷贝形式,传输效率最佳。

2.5、Linux 系统 splice 零拷贝流程

在 Linux 2.6.17 内核版本中,引入了 splice 系统调用方法,和 sendfile 方法不同的是,splice 不须要配件支持。

它将数据从磁盘读取到 OS 内核缓冲区后,内核缓冲区和 socket 缓冲区之间建设管道来传输数据,防止了两者之间的 CPU 拷贝操作。

整个拷贝环节,可以用如下流程图来形容!

Linux 系统 splice 拷贝流程,从上图可以得出如下论断:

Linux 系统 splice 方法逻辑拷贝,也是操作系统真正意义上的零拷贝。

03、IO 拷贝机制对比

从上方的 IO 拷贝机制可以看出,无论是传统 IO 形式,还是引入零拷贝之后,2次 DMA copy 是都少不了的,惟一的区别就是省掉 CPU 介入环节的形式不同。

以 Linux 系统为例,拷贝机制对比结果如下!

须要留意的中央是,零拷贝一切的形式,都须要操作系统支持,详细驳回哪种形式,由操作系统来选择。

04、相关概念说明

上文中咱们提到了几个很少见的名词,比如 DMA,用户空间,内核空间等。它们究竟是什么意思呢?

4.1、DMA 引见

DMA,英文全称是 Direct Memory Access,即间接内存访问。DMA 实质上是一块主板上独立的芯片,准许外设设备和内存存储器之间间接启动 IO 数据传输,其环节不须要 CPU 的介入。

4.2、内核空间和用户空间引见

操作系统的外围是内核,与一般的运行程序不同,它可以访问受包全的内存空间,也有访问底层配件设备的权限。

为了防止用户进程间接操作内核,保障内核安保,操作系统将虚构内存划分为两局部,一局部是内核空间(Kernel-space),一局部是用户空间(User-space)。在 Linux 系统中,内核模块运转在内核空间,对应的进程处于内核态;而用户程序运转在用户空间,对应的进程处于用户态。

内核空间总是驻留在内存中,它是为操作系统的内核保管的。运行程序是不准许间接在该区域启动读写或间接调用内核代码定义的函数。

当启动某个运行程序时,操作系统会给运行程序调配一个独自的用户空间,其实就是一个用户独享的虚构内存,每个一般的用户进程之间的用户空间是齐全隔离的、不共享的,当用户进程完结的时刻,用户空间的虚构内存也会随之监禁。

同时处于用户态的进程不能访问内核空间中的数据,也不能间接调用内核函数的,假设要调用系统资源,就要将进程切换到内核态,由内核程序来启动操作。

05、Java 零拷贝成功引见

Linux 提供的零拷贝技术,Java 并不是所有支持,目前只支持以下 2 种。

5.1、Java NIO 对 mmap 的支持

Java NIO 有一个MappedByteBuffer的类,可以用来成功内存映射。它的底层是调用了 Linux 内核的mmap的 API。

实例代码如下:

 static void mainString args {try {FileChannel readChannel  FileChannelPathsget StandardOpenOptionMappedByteBuffer   readChannelmapFileChannelMapModeREAD_ONLY      FileChannel writeChannel  FileChannelPathsget StandardOpenOption StandardOpenOptionwriteChannelreadChannelwriteChannel}catch Exception e{SystemprintlnegetMessage}}

其中MappedByteBuffer的作用,就是将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射,读取小文件,效率并不高;然而读取大文件,效率很高。

5.2、Java NIO 对 sendfile 的支持

Java NIO 中的FileChannel.transferTo方法,底层调用的就是 Linux 内核的sendfile系统调用方法。Kafka 这个开源名目就用到它,往常面试的时刻,假设面试官问起你为什么这么快,就可以提到sendfile零拷贝系统调用方法来回答。

实例代码如下:

 static void mainString args {try {FileChannel srcChannel  FileChannelPathsget StandardOpenOptionFileChannel destChannel  FileChannelPathsget StandardOpenOption StandardOpenOptionsrcChanneltransferTo srcChannelsize destChannelsrcChanneldestChannel} catch Exception e {SystemprintlnegetMessage}}

Java NIO 提供的FileChannel.transferTo并不保障必定能经常使用零拷贝。实践上能否能经常使用零拷贝与操作系统相关,假设操作系统提供sendfile这样的零拷贝系统调用方法,那么会充沛应用sendfile零拷贝的长处,否则并不能成功零拷贝。

06、小结

本位重要围绕零拷贝的逻辑,联合网友的常识分享,启动一次性常识整顿和总结。

从上方的内容总结可以看出,所谓的零拷贝,其目的并不是说不须要拷贝数据,而是经过一些手腕省略 CPU 拷贝环节,缩小了不用要的拷贝次数,优化数据拷贝效率。

以 Linux 操作系统为例,真正意义上大家比拟认可的零拷贝重要有 sendfile、splice 等方法,它们齐全经过 DMA 控制器来成功数据的拷贝,无需 CPU 来介入数据拷贝的环节,这个环节被称为零拷贝。

然而比拟糟心的是,一些机构、团队,在产品推行中适度包装零拷贝这个概念,名曰“驳回零拷贝,功能有多初等等...”,这样的宣传仿佛会让很多人感觉很凶猛。

作为一线技术者,应该多多深化了解,识透其假相,弄清楚是倾向于优化数据操作,还是真正切合场景、灵敏运用了操作系统意义上的零拷贝,大家可以多深化剖析。

07、参考

1、

2、

您可能还会对下面的文章感兴趣: