Reference:  https://segmentfault.com/a/1190000011989008

零拷贝是什么?

维基百科对“零拷贝”是这样描述的:

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.

“零拷贝” 描述的是CPU不执行拷贝数据从一块内存区域到另一块区域的任务的计算机操作。它通常用于在网络上传输文件时节省CPU周期和内存带宽。简单来说,零拷贝就是一种避免 CPU 将数据从一块存储拷贝到另外一块存储的技术。

为什么需要零拷贝技术?

通常我们会有这样的需求:将本地磁盘上的一个文件通过网络发送给远端的另一个服务。在传统的I/O中,我们通过一张图来看一下操作系统都会发生什么:

  1. 发出read()系统调用,这时处理器会从用户空间切换至内核空间;

  2. 向磁盘请求数据;

  3. 通过DMA将文件从磁盘上读取到内核空间缓冲区;

  4. read()系统调用返回,将数据从内核空间缓冲区拷贝至用户空间缓冲区,这时候处理器会从内核空间切换至用户空间;

  5. 发出write()系统调用,并将数据从用户空间缓冲区拷贝至目标socket 在内核空间的缓冲区,这时候处理器会从用户空间切换至内核空间;

  6. write()调用返回;

  7. 通过DMA将数据从内核空间缓冲区中拷贝至协议引擎(该操作是独立且异步的)。

总的来说:传统的I/O操作在整个过程中将会产生4次上下文切换和4次数据拷贝。

Q:有人可能会问, 为什么write()调用会先返回,难道他会在数据传输前返回?
A:事实上调用的返回并不保证数据被传输,甚至他并不保证传输的开始,只是意味着以太网驱动程序在其传输队列中有空位,并已经接受我们的将要传输的数据。在我们之前很可能还有很多数据包在排除。除非驱动程序或硬件实现优先级环或队列,否则数据都将以先进先出的方式被传输。

了解了传统I/O的操作后,我们再来观察一下整个过程,我们会注意到第二次和第三次数据拷贝是完全没有意义的,应用程序仅仅缓存了一下数据就又原封不动的把它发送给了目标socket 缓冲区。而且这两次拷贝是需要CPU全程参与的,从操作系统的角度来说,如果 CPU 一直被占用着去执行这项简单的任务,那么这将会是很浪费资源的;如果有其他比较简单的系统部件可以代劳这件事情,从而使得 CPU 解脱出来可以做其他的事情,那么系统资源的利用则会更加有效。

“零拷贝”正是通过消除这些多余的拷贝来提升性能的。在数据传输的过程中,避免数据在内核空间缓冲区和用户空间缓冲区之间进行拷贝,以及数据在内核空间缓冲区内的CPU拷贝。


零拷贝的实现机制

Linux 中提供类似的系统调用主要有 sendfile()、mmap() 和splice()(本文对该系统调用暂不做讨论)。

通过sendfile()实现的零拷贝

sendfile系统调用在内核版本2.1中被引入,目的是简化通过网络在两个本地文件之间进行的数据传输过程。sendfile系统调用的引入,不仅减少了数据复制,还减少了上下文切换的次数。为了更好的说明,请看下图:

  1. 发出sendfile()系统调用,这时处理器会从用户空间切换至内核空间;

  2. 向磁盘请求数据;

  3. 通过DMA将文件从磁盘上读取到内核空间缓冲区;

  4. 将数据从内核空间缓冲区拷贝到目标socket缓冲区;

  5. Sendfile()返回,这时处理器从内核空间切换至用户空间;

  6. 通过DMA将数据从目标socket缓冲区拷贝至协议引擎。

总结一下这种实现,整个过程产生了2次上下文切换和3次数据拷贝(其中2次DMA拷贝和1次CPU拷贝)。

该实现虽然减少了2次上下文切换,但仍然还有1次CPU拷贝。那这次拷贝是不是也可以省掉呢?答案是肯定的。但是需要底层操作系统的一些支持。那就是带有DMA收集功能的sendfile实现的零拷贝。

带有DMA收集功能的sendfile实现的零拷贝

从Linux2.4开始,操作系统底层提供了带有scatter/gather的DMA来从内核空间缓冲区中将数据读取到协议引擎中。这就意味着等待传输的数据不需要在连续存储器中,它可以分散在不同的内存位置。那这样一来,从文件中读出的数据就不必拷贝至目标socket的缓冲区中,只需要将缓冲区描述符添加到目标socket的缓冲区中,DMA收集操作会根据缓冲区描述符中的信息将内核空间缓冲区中的数据读取到协议引擎。这种方法不仅减少了上下文切换、还减少了由CPU参与的数据拷贝。为了更好的理解这种方法所涉及的操作,请看下图:

  1. 发出sendfile()系统调用,处理器从用户空间切换至内核空间;

  2. 通过DMA将数据copy至内核空间缓冲区;

  3. 将数据在内核空间缓冲区的地址和偏移量拷贝至目标socket的缓冲区;

  4. Sendfile()返回,处理器从内核空间切换至用户空间。

  5. 带有scatter/gather 功能的DMA将数据直接从内核缓冲区读取到协议引擎,从而消除了最后一次CPU拷贝。

总结一下,这种方法产生了2次上下文切换和2次数据拷贝。

这时有人可能会问,如果我把数据从磁盘上读出来后,再编辑一下,再发送出去,以上所说的零拷贝岂不是不能实现?
对于该问题,Linux提供了mmap来实现。

通过mmap实现的零拷贝

mmap(内存映射):mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。

  1. 发出mmap()系统调用,处理器从用户空间切换至内核空间。

  2. 向磁盘请求数据;

  3. 通过DMA将数据从磁盘拷贝至内核空间缓冲区;

  4. mmap()调用返回,这时候用户程序和操作系统共享这个缓冲区,不需要再将数据从kernel buffer 拷贝至 user buffer,处理器从内核空间切换至用户空间;

  5. 用户逻辑处理;

  6. 发出write()系统调用,将数据从内核空间缓冲区拷贝至目标socket缓冲区,这时处理器从用户空间切换至内核空间;

  7. write()调用返回,处理器从内核空间切换至用户空间;

  8. 通过DMA将数据拷贝至协议引擎。

总结一下:这种方法将产生4次上下文切换和3次数据拷贝。

至此,零拷贝技术就介绍完了。本文所提及的零拷贝技术都是需要底层操作系统支持的,同时,零拷贝技术一直是在不断地发展和完善当中的,本文并没有涵盖 Linux 上出现的所有零拷贝技术。

Linux下的零拷贝的更多相关文章

  1. Linux 中的零拷贝技术,第 2 部分

    技术实现 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.第一部分主要介绍了一些零拷贝技术的相关背景知识,简要概 ...

  2. Linux 中的零拷贝技术,第 1 部分

    概述 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.本文是本系列文章的第一部分,主要是介绍一些零拷贝技术的相关 ...

  3. 【转】浅析Linux中的零拷贝技术

    本文探讨Linux中主要的几种零拷贝技术以及零拷贝技术适用的场景.为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入: 引文## 在写一个服务端程序时(Web Server或者文件服务器),文件 ...

  4. [转帖]Linux 中的零拷贝技术,第 2 部分

    Linux 中的零拷贝技术,第 2 部分 https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy2/index.html   Linux 中 ...

  5. [转帖]Linux 中的零拷贝技术,第 1 部分

    Linux 中的零拷贝技术,第 1 部分 https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html   引言 传统的 ...

  6. Linux中的零拷贝

    零拷贝 本文图片和一些内容均来自后面的参考,非原创只是把文章中的一些关键内容整理一下,算作是一个学习笔记. 传统的I/O操作 传统的IO操作是用户应用程序只是需要调用两个系统调用 read() 和 w ...

  7. Linux中的零拷贝技术

    转载:https://www.jianshu.com/p/fad3339e3448 引文## 在写一个服务端程序时(Web Server或者文件服务器),文件下载是一个基本功能.这时候服务端的任务是: ...

  8. Linux文件和零拷贝

    本文转载自文件和零拷贝 文件概述 文件描述符 文件描述符:在Linux中,所有的文件都是通过文件描述符引用.fd是一个非负整数.按照惯例,标准输入的fd是0,标准输出的fd是1,标准错误的fd是2.分 ...

  9. 浅析 Linux 中的零拷贝技术

    本文探讨Linux中 主要的几种零拷贝技术 以及零拷贝技术 适用的场景 .为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入: 引文 在写一个服务端程序时(Web Server或者文件服务器), ...

随机推荐

  1. c#读取数据库bool值

    数据库里bit这个布尔类型的字段,非常实用,但是在c#里读取时,许多人喜欢犯一些错误,导致运行报错. 实际中,有效的正确读取方法只有以下两种: int xxx= Convet.ToInt16(read ...

  2. web技术栈开发原生应用-多端共用一套代码

    weex: vuejs开发原生应用 nativescript: vuejs开发原生应用 ReactNative = reactjs开发原生应用 ionic = angularjs 开发原生应用

  3. 使用GitHub搜索技巧

    in:name example 名字中有"example"in:readme example readme中有"example"in:description e ...

  4. MySQL之简介以及数据类型(一)

    一:关系型数据库 所谓的关系型数据库RDBMS,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据. 二:关系型数据库的主要产品: oracle:在以前的大型项目中使用 ...

  5. springMVC执行流程及架构

    目录 springMVC简单执行流程 springMVC框架 注解实现 springMVC简单执行流程 springMVC框架 执行流程: 1.用户发送请求至前端控制器DispatcherServle ...

  6. 31、splice()用法

    改变数组,向数组中添加/删除元素: eg: 1.删除元素: let arr=['bob','lily','bike','sam']; arr.splice(2,1) console.log(arr) ...

  7. 英文INSURAUNCE保险INSURAUNCE词汇

    世界保险INSURAUNCE 人类社会从开始就面临着自然灾害和意外事故的侵扰,在与大自然抗争的过程中,古代人们就萌生了对付灾害事故的保险思想和原始形态的保险方法.公元前2500年前后,古巴比伦王国国王 ...

  8. Cheat Engine 自动注入

    打开游戏 引用自动注入 选择跳转地址 CEAA脚本自动生成 红色部分就是添加代码的地方 添加代码 让阳光每次减少0,并且分配到作弊表 进行激活测试 发现阳光果然只增不减了

  9. sizeof()计算

    本节包含sizeof()计算结构体,位域,数组,字符串,指针,c++中的class等类型的大小,sizeof()计算的大小都是以字节为单位. 一 计算基本类型的长度 sizeof(char): 1 s ...

  10. Solr基础理论【排名检索、查准率、查全率】

    一.排名检索 搜索引擎代表了基于查询,返回优先文档的一种方法.在关系型数据库的SQL查询中,表的一行要么匹配一个查询,要么不匹配,查询结果基于一列或多列排序.搜索引擎根据文档与查询匹配的程度为文档打分 ...