Linux下的零拷贝
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中,我们通过一张图来看一下操作系统都会发生什么:
发出read()系统调用,这时处理器会从用户空间切换至内核空间;
向磁盘请求数据;
通过DMA将文件从磁盘上读取到内核空间缓冲区;
read()系统调用返回,将数据从内核空间缓冲区拷贝至用户空间缓冲区,这时候处理器会从内核空间切换至用户空间;
发出write()系统调用,并将数据从用户空间缓冲区拷贝至目标socket 在内核空间的缓冲区,这时候处理器会从用户空间切换至内核空间;
write()调用返回;
通过DMA将数据从内核空间缓冲区中拷贝至协议引擎(该操作是独立且异步的)。
总的来说:传统的I/O操作在整个过程中将会产生4次上下文切换和4次数据拷贝。
Q:有人可能会问, 为什么write()调用会先返回,难道他会在数据传输前返回?
A:事实上调用的返回并不保证数据被传输,甚至他并不保证传输的开始,只是意味着以太网驱动程序在其传输队列中有空位,并已经接受我们的将要传输的数据。在我们之前很可能还有很多数据包在排除。除非驱动程序或硬件实现优先级环或队列,否则数据都将以先进先出的方式被传输。
了解了传统I/O的操作后,我们再来观察一下整个过程,我们会注意到第二次和第三次数据拷贝是完全没有意义的,应用程序仅仅缓存了一下数据就又原封不动的把它发送给了目标socket 缓冲区。而且这两次拷贝是需要CPU全程参与的,从操作系统的角度来说,如果 CPU 一直被占用着去执行这项简单的任务,那么这将会是很浪费资源的;如果有其他比较简单的系统部件可以代劳这件事情,从而使得 CPU 解脱出来可以做其他的事情,那么系统资源的利用则会更加有效。
“零拷贝”正是通过消除这些多余的拷贝来提升性能的。在数据传输的过程中,避免数据在内核空间缓冲区和用户空间缓冲区之间进行拷贝,以及数据在内核空间缓冲区内的CPU拷贝。
零拷贝的实现机制
Linux 中提供类似的系统调用主要有 sendfile()、mmap() 和splice()(本文对该系统调用暂不做讨论)。
通过sendfile()实现的零拷贝
sendfile系统调用在内核版本2.1中被引入,目的是简化通过网络在两个本地文件之间进行的数据传输过程。sendfile系统调用的引入,不仅减少了数据复制,还减少了上下文切换的次数。为了更好的说明,请看下图:
发出sendfile()系统调用,这时处理器会从用户空间切换至内核空间;
向磁盘请求数据;
通过DMA将文件从磁盘上读取到内核空间缓冲区;
将数据从内核空间缓冲区拷贝到目标socket缓冲区;
Sendfile()返回,这时处理器从内核空间切换至用户空间;
通过DMA将数据从目标socket缓冲区拷贝至协议引擎。
总结一下这种实现,整个过程产生了2次上下文切换和3次数据拷贝(其中2次DMA拷贝和1次CPU拷贝)。
该实现虽然减少了2次上下文切换,但仍然还有1次CPU拷贝。那这次拷贝是不是也可以省掉呢?答案是肯定的。但是需要底层操作系统的一些支持。那就是带有DMA收集功能的sendfile实现的零拷贝。
带有DMA收集功能的sendfile实现的零拷贝
从Linux2.4开始,操作系统底层提供了带有scatter/gather的DMA来从内核空间缓冲区中将数据读取到协议引擎中。这就意味着等待传输的数据不需要在连续存储器中,它可以分散在不同的内存位置。那这样一来,从文件中读出的数据就不必拷贝至目标socket的缓冲区中,只需要将缓冲区描述符添加到目标socket的缓冲区中,DMA收集操作会根据缓冲区描述符中的信息将内核空间缓冲区中的数据读取到协议引擎。这种方法不仅减少了上下文切换、还减少了由CPU参与的数据拷贝。为了更好的理解这种方法所涉及的操作,请看下图:
发出sendfile()系统调用,处理器从用户空间切换至内核空间;
通过DMA将数据copy至内核空间缓冲区;
将数据在内核空间缓冲区的地址和偏移量拷贝至目标socket的缓冲区;
Sendfile()返回,处理器从内核空间切换至用户空间。
带有scatter/gather 功能的DMA将数据直接从内核缓冲区读取到协议引擎,从而消除了最后一次CPU拷贝。
总结一下,这种方法产生了2次上下文切换和2次数据拷贝。
这时有人可能会问,如果我把数据从磁盘上读出来后,再编辑一下,再发送出去,以上所说的零拷贝岂不是不能实现?
对于该问题,Linux提供了mmap来实现。
通过mmap实现的零拷贝
mmap(内存映射):mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。
发出mmap()系统调用,处理器从用户空间切换至内核空间。
向磁盘请求数据;
通过DMA将数据从磁盘拷贝至内核空间缓冲区;
mmap()调用返回,这时候用户程序和操作系统共享这个缓冲区,不需要再将数据从kernel buffer 拷贝至 user buffer,处理器从内核空间切换至用户空间;
用户逻辑处理;
发出write()系统调用,将数据从内核空间缓冲区拷贝至目标socket缓冲区,这时处理器从用户空间切换至内核空间;
write()调用返回,处理器从内核空间切换至用户空间;
通过DMA将数据拷贝至协议引擎。
总结一下:这种方法将产生4次上下文切换和3次数据拷贝。
至此,零拷贝技术就介绍完了。本文所提及的零拷贝技术都是需要底层操作系统支持的,同时,零拷贝技术一直是在不断地发展和完善当中的,本文并没有涵盖 Linux 上出现的所有零拷贝技术。
Linux下的零拷贝的更多相关文章
- Linux 中的零拷贝技术,第 2 部分
技术实现 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.第一部分主要介绍了一些零拷贝技术的相关背景知识,简要概 ...
- Linux 中的零拷贝技术,第 1 部分
概述 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.本文是本系列文章的第一部分,主要是介绍一些零拷贝技术的相关 ...
- 【转】浅析Linux中的零拷贝技术
本文探讨Linux中主要的几种零拷贝技术以及零拷贝技术适用的场景.为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入: 引文## 在写一个服务端程序时(Web Server或者文件服务器),文件 ...
- [转帖]Linux 中的零拷贝技术,第 2 部分
Linux 中的零拷贝技术,第 2 部分 https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy2/index.html Linux 中 ...
- [转帖]Linux 中的零拷贝技术,第 1 部分
Linux 中的零拷贝技术,第 1 部分 https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html 引言 传统的 ...
- Linux中的零拷贝
零拷贝 本文图片和一些内容均来自后面的参考,非原创只是把文章中的一些关键内容整理一下,算作是一个学习笔记. 传统的I/O操作 传统的IO操作是用户应用程序只是需要调用两个系统调用 read() 和 w ...
- Linux中的零拷贝技术
转载:https://www.jianshu.com/p/fad3339e3448 引文## 在写一个服务端程序时(Web Server或者文件服务器),文件下载是一个基本功能.这时候服务端的任务是: ...
- Linux文件和零拷贝
本文转载自文件和零拷贝 文件概述 文件描述符 文件描述符:在Linux中,所有的文件都是通过文件描述符引用.fd是一个非负整数.按照惯例,标准输入的fd是0,标准输出的fd是1,标准错误的fd是2.分 ...
- 浅析 Linux 中的零拷贝技术
本文探讨Linux中 主要的几种零拷贝技术 以及零拷贝技术 适用的场景 .为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入: 引文 在写一个服务端程序时(Web Server或者文件服务器), ...
随机推荐
- RabbitMQ学习之Publish/Subscribe(3)
上一个教程中,我们创建了一个work queue. 其中的每个task都会被精确的传送到一个worker. 这节,我们将会讲把一个message传送到多个consumers. 这种模式叫做publis ...
- BZOJ3209: 花神的数论题(数位DP)
题目: 3209: 花神的数论题 解析: 二进制的数位DP 因为\([1,n]\)中每一个数对应的二进制数是唯一的,我们枚举\(1\)的个数\(k\),计算有多少个数的二进制中有\(k\)个\(1\) ...
- VS 对话框控件的Tab顺序问题
我们先来直观的看看各个控件的Tab顺序吧.打开“Resource View”视图,然后在资源中找到对话框IDD_ADDITION_DIALOG,双击ID后中间客户区域出现其模板视图.在主菜单中选择“F ...
- 阿里和Google的JAVA开发规约
<阿里 JAVA开发规约> 阿里巴巴Java开发手册终极版v1.3.0.pdf 出处:github地址:https://github.com/alibaba/p3c <Google ...
- Oracle ERP 库存管理(业务流程 核心流程)
库存核心业务 库存管理的核心是对货物本身的管理,是对货物的数量与相关属性的管理,目的是为销售与采购服务,确保合理的库存保有量,处理库存分类帐目与进出流水帐,以单据的形式基本涵盖仓库的各种进出库业务. ...
- IDEA编码时卡顿问题
当代码行数超过2000行,甚至更多时会出现编码时卡顿到无法编译的情况,解决方法如下: 方法1:修改IDEA配置参数 找到D:\ideaIU-2019.1.1.win\bin\idea64.exe.vm ...
- 微信小程序获取用户手机号 记录 (PHP)
1. 用户登录时需要获取 openid ,同时可以获取 session_key, 二者同时返回, 此时我们要将二者存储在服务端. 2. 小程序端 button 按钮拉起授权, 向api 传递 iv 和 ...
- Nginx + PHP 修改单次请求 最大执行时间
1. php.ini (usr/local/php/etc) max_execution_time = 2. php-fpm.conf (usr/local/php/etc) request_t ...
- python统计两个字符串从首字符开始最大连续相同的字符数
在python中统计两个字符串从首字符开始最大连续相同的字符数,函数如下: def get_num(s1, s2): num = 0 len_s1 = len(s1) list_s1 = [] for ...
- eval用法
在shell的学习中,我们会遇到这两种符号:反引号(` `)和$(),那么它们之间有什么区别和联系呢? 我们都知道在bash中,反引号和$()都是用来做命令替换的,命令替换就是用来重组命令行,先完成引 ...