要理解零拷贝机制,首先需要了解它所要解决的问题,试想一个场景:我们需要从磁盘读取一个文件通过网络输出到一个客户端。

服务端的步骤一般是这样的:

  1. read(file, tmp_buf, len);

  2. write(socket, tmp_buf, len);

虽然只有两个步骤:从磁盘读取文件,将文件写入到socket,但是在操作系统内部经历了一个较为复杂的过程,这个过程如下图所示:

上面部分显示,这个过程经历了四次上下文切换。下面部分显示,经历了四次数据拷贝过程:

  1. 数据从磁盘复制到内核缓冲区
  2. 从内核缓冲区复制到用户空间缓冲区
  3. 从用户缓冲区复制到内核的socket缓冲区
  4. 从socket缓冲区复制到协议引擎(这里是网卡驱动)

这里 要把数据从磁盘复制到内核缓冲区是必须的,因为系统需要读取数据输出给网卡嘛。但是为啥还要从内核复制一份到用户空间呢?应用程序直接使用内核缓冲区的数据不就行了吗?这是因为对于操作系统来说,可能有多个应用程序会同时使用这些数据,并有可能进行修改,如果让大家都使用同一份内核空间的数据就会产生冲突。因此,操作系统设计为:每个应用程序想使用这些数据都必须复制一份到自己的用户空间,这样就不会互相影响了。所以这个机制在碰到数据不需要做修改的场景时就产生了浪费,数据本来可以呆在内核缓冲区不动,没必要再多此一举拷贝一次到用户空间。

为了避免这种浪费,人们一开始采用了mmap调用的方式来进行优化。即应用程序不再调用read,而是采用mmap,mmap会从磁盘复制数据到内核缓冲区,然后与用户进程共享该内核缓冲区,这样就不再需要从内核缓冲区复制到用户缓冲区了,这样就比之前少了一次数据复制过程。不过,这种改进方式存在一些问题,比如在mmap一个文件时,文件被另一个进程截断将会产生错误等。当然,人们也发明了一些方法来解决这类问题,但不管怎样都使得这个过程变得很复杂不易使用。

随着linux内核的发展,在2.1版本中引入了一个叫 sendfile的系统调用。sendfile相当于封装了(mmap,write)的过程,自动处理了文件被截断等问题,除此之外,sendfile还减少了上下文切换次数。sendfile的过程是这样的:

  1. 从磁盘读取文件内容到内核缓冲区
  2. 直接从内核缓冲区复制数据到socket缓冲区
  3. 从socket缓冲区复制到协议引擎(这里是网卡驱动)

这种方式虽好,但是仍然存在一次从内核缓冲区到内核socket缓冲区的复制行为,也就是从内存的一个区域复制到内存的另一个区域的行为,这个可以避免吗?后来人们又对硬件进行了改进,在硬件的帮助下来消除内存之间的数据复制。这种硬件需要支持一种叫“收集”操作的接口,它支持从内存中不同位置收集数据,也就是不再限定于只从内核socket缓冲区来收集数据,而是可以从内核缓冲区去收集。

在linux内核版本2.4中对sendfile调用做了一系列优化来适应这个需求,对于应用程序来说,sendfile的调用方式不需要做任何修改,但是它底层的机制有了一定的改进,如下图所示:

如图所示:数据复制到内核缓冲区以后,不再需要整个拷贝到socket缓冲区,而是只需要将数据的位置和长度信息(append dscr)传输到socket缓冲区,这样DMA引擎会根据这些信息直接从内核缓存区复制数据给协议引擎。

最终,数据只需要从磁盘复制到内存,再从内存复制到协议引擎,跟最开始相比减少了从内核到用户空间,从用户空间到socket缓冲两次复制。但是明明还有两次数据的复制,为什么要叫“零拷贝”呢?这是因为从操作系统的角度来说,数据没有从内存复制到内存的过程,也就没有了CPU参与的过程, 所以对于操作系统来说就是零拷贝了。查看wiki对零拷贝的定义如下:

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another.

从定义我们看到,零拷贝是指不需要cpu参与在内存之间复制数据的操作。那这个过程为啥不需要cpu参与呢?

仔细看上面的图示,你会发现:从磁盘复制到内核缓冲区是通过DMA引擎来做而不是cpu,同样的,从socket缓冲区到协议引擎也是由 DMA引擎来做,这样就节省了cpu的工作。而这整个过程都在内核中完成也减少了操作系统的上下文切换开销。

总结一下:

  • 存在用户空间和内核空间的交互行为就会产生上下文切换,所以即使用户空间与内核空间共享同一份缓冲区也一样。
  • 利用硬件支持的DMA引擎可以减少对cpu的使用,磁盘,网卡都有此引擎。
  • 利用硬件的“收集”接口功能可以将数据位置和长度直接传输给硬件相关的引擎,从而让硬件引擎直接从相应的内存区域读取数据。
  • 零拷贝是用于对不变的数据做传输,如果应用程序需要修改数据那势必就不能用到零拷贝了,所以零拷贝可不是万能的。实际上,零拷贝还有其它的一些限制条件,可以参考相关的资料。

除了上面所列出的零拷贝机制,linux中零拷贝技术还有以下几种:

  1. 直接IO:应用程序直接访问硬件存储,内核只辅助数据传输,不进行页缓存。
  2. 写时复制技术:当应用程序不需要修改数据时只保存在内核缓冲区,不复制到用户空间。(这类方法没有dma,需要cpu的全程参与)
  3. splice:是与sendfile类似的一种方法,适用于将数据从一个地方传送到另一个地方不需要经过应用程序的处理。splice可以在内核空间整块地移动数据,并用可以通过异步方式进行。splice允许任意两个文件之间互相连接,而sendfile只是文件到socket之间,所以sendfile只是splice的了个子集。在2.6.23版本内核中,sendfile的机制已经没有了,api相应的功能换成了splice机制来实现。

除了操作系统的零拷贝机制,在netty里还有一种称之为用户空间的零拷贝机制,但那完全是另一种原理以及解决完全不同问题的一种机制。

【参考文献】

https://www.linuxjournal.com/article/6345

https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html

https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy2/index.html

https://www.jianshu.com/p/2fd2f03b4cc3

转载于:https://my.oschina.net/u/150599/blog/3020027

零拷贝机制(Zero Copy)相关推荐

  1. 什么是零拷贝机制(Zero Copy) ?

    要理解零拷贝机制,首先需要了解它所要解决的问题,试想一个场景:我们需要从磁盘读取一个文件通过网络输出到一个客户端. 服务端的步骤一般是这样的: read(file, tmp_buf, len); wr ...

  2. linux I/O--IO原理和几种零拷贝机制(五)

    前言 零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间.它的作用是在数据报从网络设备到用户 ...

  3. 深入剖析Linux IO原理和几种零拷贝机制的实现

    本文来说下Linux IO原理和几种零拷贝机制的实现 文章目录 概述 物理内存和虚拟内存 物理内存 虚拟内存 内核空间和用户空间 内核空间 用户空间 Linux的内部层级结构 Linux I/O读写方 ...

  4. Linux IO原理和零拷贝机制

    目录 1 概述 2 Linux I/O读写方式 2.1 I/O中断原理 2.2. DMA传输原理 2.3 传统I/O方式 3 零拷贝方式 3.1 用户态直接I/O 3.2 mmap + write 3 ...

  5. 浅析操作系统和Netty中的零拷贝机制

    点击关注公众号,Java干货及时送达 零拷贝机制(Zero-Copy)是在操作数据时不需要将数据从一块内存区域复制到另一块内存区域的技术,这样就避免了内存的拷贝,使得可以提高CPU的.零拷贝机制是一种 ...

  6. 零拷贝(Zero Copy)

    一.零拷贝简介 零拷贝指的是,从一个存储区域到另一个存储区域的copy任务没有CPU参与.零拷贝通常用于网络文件传输,以减少CPU消耗和内存带宽占用,减少用户空间(用户可以操作的内存缓存区域)与CPU ...

  7. 零拷贝机制在文件传输中的使用手法

    文章目录 文件传输(读取与发送)中的拷贝与上下文切换 零拷贝技术 sendfile sendfile + SG-DMA mmap + write splice Direct I/O 经典应用 文件传输 ...

  8. 通俗易懂的Kafka零拷贝机制

    Kafka之所以那么快,其中一个很大的原因就是零拷贝(Zero-copy)技术,零拷贝不是kafka的专利,而是操作系统的升级,又比如Netty,也用到了零拷贝.下面我就画图讲解零拷贝,如果对你有帮助 ...

  9. 零拷贝(Zero Copy)技术

    概念 我们知道Linux系统分为用户态和内核态,在用户态每发起一次IO请求,就需要进行2次上下文切换(分别是用户态->内核态,内核态→用户态),和一次CPU拷贝(将数据从内核缓存拷贝到用户缓存) ...

最新文章

  1. 用人工智能打击人工智能
  2. php 匹配一次,PHP Regex匹配最后一次出现的字符串
  3. 计算机组装与维护实验指导,计算机组装与维护实验指导书.pdf
  4. TD-SCDMA智能天线波束赋形能力的讨论
  5. 第四届泰迪杯数据挖掘大赛
  6. python sys模块_Python 基础(二十):sys 模块
  7. java字体颜色编程_Java 字体颜色转换工具类 ColorUtil
  8. Windows中文编码显示问题集锦
  9. Spring.net(一)----Spring.NET框架简介及模块说明
  10. mongodb添加登陆验证
  11. H264封装为FLV
  12. 一条命令关闭极域电子教室学生端
  13. implement在JAVA中_java中的implement
  14. js pug 代码_PUG 系列 | 第二课 JS 代码混合、包含引入
  15. Fastadmin隐藏后台登录入口地址?
  16. 人工智能第一讲:发展历史图解
  17. android打开app白色页面,完美解决Android App启动页有白屏闪过的问题
  18. ChatGPT深度体验记录,期待GPT-4(测试各领域知识,正常聊天,写代码,写诗歌,模拟人格,机器翻译,语法改错等)
  19. 怎样恢复计算机管理员用户,忘记了电脑系统Administrator账户的密码?如何恢复?...
  20. BIOS入门基础-----BIOS阶段划分

热门文章

  1. PHP请求库Guzzle配置代理
  2. 可以举一反三的数学题
  3. php生成11位不重复数字,【PHP】php生成一个不重复的数字(订单号、会员号)
  4. JavaScript中解释/\s/g
  5. 个人导航网源码 官方权威版本
  6. cfc 教程_Google CFC
  7. Java PC端微信、支付宝扫码支付(二)
  8. ssm用ajax校验用户名,SSM之检验用户名是否重复
  9. uniapp的条件编译
  10. slam学习笔记七----IMU传感器