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#条码生成及打印实例代码

    ";//条码 ; ; //打印按钮 private void button1_Click(object sender, EventArgs e) { //实例化打印对象 PrintDocum ...

  2. chrome安装插件

    看了很多的解决办法,也试了很多种,有的有点用,有的只能用一次,最后成功了,在这里总结一下 一.首先下载 很多人下载的都ctx格式的,然后拖进去安装,用了一次就崩溃了,主要还是安装方式不对,一般需要将下 ...

  3. C#文件操作之把字符串取到文本文件及把文本文件读取到字符串中

    一.把字符串读取到文本文件中 using (FileStream fs = new FileStream(Path, FileMode.OpenOrCreate))//把json读到一个文本中 { S ...

  4. CodeForces 955D Scissors

    昨晚CF比赛比较颓,今天有心情写题解就不错了QWQ 洛谷题目页面传送门 & CodeForces题目页面传送门 给定字符串\(a,b,|a|=n,|b|=m\),求是否可以在\(a\)中选\( ...

  5. css 平行四边形

    平行四边形 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  6. vue 子组件 $emit方法 调用父组件方法

    $emit方法 父组件 <template> <div> <child @callFather="activeSon"></child&g ...

  7. Prometheus学习笔记(1)Prometheus架构简介

    Prometheus简介和架构 Prometheus 是由 SoundCloud 开源监控告警解决方案.架构图如下: 如上图,Prometheus主要由以下部分组成: Prometheus Serve ...

  8. Scrapy爬虫提高效率

    如何提高scrapy的爬取效率 增加并发: 默认scrapy开启的并发线程为32个,可以适当进行增加.在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发 ...

  9. 详解php概念以及主配置文件

    浏览器仅能够解码HTML格式的文档,对于非HTML格式的文档,浏览器调用插件或者通过CGI接口调用其他程序来解码. 动态网站: 我们在服务器端或客户端执行了一段脚本或者一段程序,这段程序执行的结果根据 ...

  10. (六)Kubernetes Pod控制器-ReplicaSet和Deployment和DaemonSet

    Pod控制器相关知识 控制器的必要性 自主式Pod对象由调度器调度到目标工作节点后即由相应节点上的kubelet负责监控其容器的存活状态,容器主进程崩溃后,kubelet能够自动重启相应的容器.但对出 ...