零拷贝详解

1、在没有DMA技术之前的I/O过程是这样的:

  • CPU发出对应的指令给磁盘控制器,然后返回
  • 磁盘控制器收到指令后,于是就开始准备数据,会把数据放入到磁盘控制器的内部缓冲区,然后产生中断
  • CPU收到中断信号后,停下手头工作,接着把磁盘控制器的缓冲区的数据一次一个字节地读进自己的寄存器。然后再把寄存器里的数据写入到内存,而在数据传输的期间CPU是无法执行其他任务 的。

在这里插入图片描述

整个传输过程中都要CPU亲自参与搬运数据的过程,而且这个过程,CPU是不能做其他事情的。这会大大降低CPU的效率,并且如果使用千兆网卡或者硬盘传输大量数据时,都用CPU搬运的话,肯定忙不过来

2、DMA技术

直接内存访问 (Direct Memory Access) ,简单理解就是,在进行I/O设备和内存的数据传输的时候,数据搬运的工作全部交给DMA控制器,而CPU不再参与任何与数据搬运相关的事情,这样CPU就可以去处理其他的事务。

在这里插入图片描述

与磁盘的交互(IO操作)都交给了DMA控制器去做,CPU得到解放

具体过程:

  • ⽤户进程调⽤ read ⽅法,向操作系统发出 I/O 请求,请求读取数据到⾃⼰的内存缓冲区中,进程进⼊阻塞状态;
  • 操作系统收到请求后,进⼀步将 I/O 请求发送 DMA ,然后让 CPU 执⾏其他任务;
  • DMA 进⼀步将 I/O 请求发送给磁盘;
  • 磁盘收到 DMA 的 I/O 请求,把数据从磁盘读取到磁盘控制器的缓冲区 中,当磁盘控制器的缓冲区被读满后,向 DMA 发起中断信号,告知⾃⼰缓冲区已满;
  • DMA 收到磁盘的信号,将磁盘控制器缓冲区中的数据拷⻉到内核缓冲区中 ,此时不占⽤ CPU,CPU 可以执⾏其他任务;
  • 当 DMA 读取了⾜够多的数据,就会发送中断信号给 CPU;
  • CPU 收到 DMA 的信号,知道数据已经准备好,于是将数据从内核拷⻉到⽤户空间 ,系统调⽤返回

早期DMA只存在于主板上,如今IO设备越来越多,数据传输的需求也不尽相同,所以每个I/O设备里面都有自己的DMA控制器

3、传统的文件传输

如果服务端要提供文件传输的功能,我们能想到的最简单的方式是:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。

而传统的I/O的工作方式是,数据读取和写入是从用户空间到内核空间来回复制,而内核空间的数据是通过操作系统的I/O接口从磁盘读取或写入。

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

这两行代码干了非常多的事,如图:

在这里插入图片描述

可以看到,这期间发生了4次用户态与内核态的上下文切换 ,因为发生了两次系统调用,一次是read(),一次是write()。每一次系统调用,都要从用户态切换到内核态,等内核态完成任务后又要切换回用户态。而上下文切换的成本也很大,尤其在高并发的场景下,这类时间容易被累积放大,从而影响系统的性能。

其次,还发生了4次数据拷贝 ,两次是DMA拷贝,两次是CPU拷贝。

  • 第⼀次拷⻉,把磁盘上的数据拷⻉到操作系统内核的缓冲区⾥,这个拷⻉的过程是通过 DMA 搬运的。
  • 第⼆次拷⻉,把内核缓冲区的数据拷⻉到⽤户的缓冲区⾥,于是我们应⽤程序就可以使⽤这部分数据了,这个拷⻉到过程是由 CPU 完成的。
  • 第三次拷⻉,把刚才拷⻉到⽤户的缓冲区⾥的数据,再拷⻉到内核的 socket 的缓冲区⾥,这个过程依然还是由 CPU 搬运的。
  • 第四次拷⻉,把内核的 socket 缓冲区⾥的数据,拷⻉到⽹卡的缓冲区⾥,这个过程⼜是由 DMA 搬运的

只是搬运一份数据,结果却进行了四次数据拷贝,过多的数据拷贝会消耗CPU资源,大大降低系统性能。

这种传统的文件传输存在冗余的上下文切换和拷贝次数!

优化文件传输

1、如何减少用户态与内核态的上下文切换的次数

读取磁盘数据时,之所以要发生上下文切换,这是因为用户空间没有权限操作磁盘或网卡,内核的权限最高。所以一般要通过内核去完成某些任务的时候,就需要使用操作系统提供的系统调用函数。

而一次系统调用必然会发生2次上下文切换:从用户态切换到内核态,内核态完成任务后再切换回用户态

所以要减少上下文切换的次数就要减少系统调用的次数

2、如何减少数据拷贝的次数

传统的文件传输过程会经过四次数据拷贝,而这其中,从内核的读缓冲区拷贝到用户的缓冲区中,再从用户的缓冲区拷贝到socket的缓冲区 ,这个过程是没有必要的。因为文件传输的应用场景中,在用户空间我们并不会对数据再加工 ,可以省去拷贝到数据缓冲区这一步。

零拷贝

1、实现零拷贝技术的方式通常有2种:

  • mmap+write
  • sendfile

它们是如何减少上下文切换和数据拷贝的次数?

2、mmap+write

read()系统调用的过程,会将内核缓冲区的数据拷贝到用户的缓冲区,为了减少这一步开销,我们可以用mmap()替换read()系统调用函数。

buf = mmap(file, len);
write(sockfd, buf, len);

mmap()系统调用函数会直接把内核缓冲区里的数据映射到用户空间,这样操作系统内核与用户空间就不需要再进行任何的数据拷贝操作

这样做会将内核的读缓冲区拷贝到用户缓冲区,再从用户缓冲区拷贝到socket的缓冲区 这两次拷贝变成内核缓冲区拷贝到socket缓冲区 这一次拷贝

即从原来的四次拷贝变为三次拷贝,减少了一次数据拷贝的过程。

但这并不是理想的零拷贝,因为仍然需要通过CPU把内核缓冲区的数据拷贝到socket缓冲区中,而且仍然需要4次上下文切换

3、sendfile

在Linux内核版本2.1中,提供了一个专门发送文件的系统调用函数sendfile()。

  • ⾸先,它可以替代前⾯的 read() 和 write() 这两个系统调⽤,这样就可以减少⼀次系统调⽤ ,也就减少了 2 次上下⽂切换的开销。
  • 其次,该系统调⽤,可以直接把内核缓冲区⾥的数据拷⻉到 socket 缓冲区⾥ ,不再拷⻉到⽤户态,这样就只有 2 次上下⽂切换,和 3 次数据拷⻉。

从 Linux 内核 2.4 版本开始起,对于⽀持⽹卡⽀持 SG-DMA 技术的情况下, sendfile() 系统,调⽤的过程发⽣了点变化,具体过程如下:

  • 第⼀步,通过 DMA 将磁盘上的数据拷⻉到内核缓冲区⾥;(DMA拷贝)
  • 第⼆步,缓冲区描述符和数据⻓度传到 socket 缓冲区,这样⽹卡的 SG-DMA 控制器就可以直接将内核缓存中的数据拷⻉到⽹卡的缓冲区⾥ ,此过程不需要将数据从操作系统内核缓冲区拷⻉到socket 缓冲区中,这样就减少了⼀次数据拷⻉;(SG-DMA拷贝)

所以,这个过程之中,只进行了一次系统调用(sendfile(),进⾏了 2 次数据拷⻉(磁盘到内核,内核到网卡)

这就是所谓的零拷贝技术。因为我们没有在内存层面去拷贝数据,全程没有通过CPU来搬运数据

在这里插入图片描述

4、总结

实现零拷贝技术的文件传输方式相比传统文件传输的方式,减少了2次上下文切换和数据拷贝次数。只需要进行两次上下文切换和两次数据拷贝就可以完成文件的传输。并且两次数据拷贝都不要通过CPU完成,是由DMA来完成。总体来看,零拷贝技术可以把文件传输的性能提高至少一倍以上

总结

1、早期I/O操作,内存与磁盘的数据传输的工作都是由CPU完成,此时CPU不能进行其他任务,会特别浪费CPU资源

2、为了解决这一问题,出现了DMA技术。每个I/O设备都有自己的DMA控制器,通过这个DMA 控制器,CPU 只需要告诉 DMA 控制器,我们要传输什么数据,从哪⾥来,到哪⾥去,就可以放⼼离开了。后续的实际数据传输⼯作,都会由 DMA 控制器来完成,CPU 不需要参与数据传输的⼯作。

3、传统的IO工作方式,从硬盘读取数据,通过网卡向外发送 。需要进行4次用户态与内核态之间的上下文切换,4次数据拷贝。其中2次数据拷贝发生在内核的缓冲区和对应的硬件设备(磁盘、网卡)之间,由DMA完成;2次数据拷贝发送在用户态和内核态之间,由CPU完成。这种传输方式有冗余的上下文切换次数和数据拷贝次数!

4、对于文件传输的优化,实现零拷贝。通过一次系统调用(sendfile)合并了磁盘读取(read)和网络发送(write)两个操作 ,降低了上下文切换次数;只进行了两次数据拷贝,从磁盘文件到内核缓冲区,从内核缓冲区到网卡,都是由DMA搬运,降低了数据拷贝次数

5、零拷贝技术是基于 PageCache 的,PageCache 会缓存最近访问的数据,提升了访问缓存数据的性能,同时,为了解决机械硬盘寻址慢的问题,它还协助 I/O 调度算法实现了 IO 合并与预读,这也是顺序读⽐随机读性能好的原因。这些优势,进⼀步提升了零拷⻉的性能。

6、当传输大文件时,不能使用零拷贝,因为可能由于 PageCache 被⼤⽂件占据,⽽导致「热点」小文件无法利用到 PageCache,并且大文件的缓存命中率不⾼,这时就需要使用「异步 IO + 直接 IO 」的方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/76145.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python opennsfw/opennsfw2 图片/视频 鉴黄 笔记

nsfw&#xff08; Not Suitable for Work&#xff09;直接翻译就是 工作的时候不适合看&#xff0c;真文雅 nsfw效果&#xff0c;注意底部的分数 大体流程&#xff0c;输入图片/视频&#xff0c;输出0-1之间的数字&#xff0c;一般情况下&#xff0c;Scores < 0.2 认为是非…

OpenCV-Python中的图像处理-霍夫变换

OpenCV-Python中的图像处理-霍夫变换 霍夫变换霍夫直线变换霍夫圆环变换 霍夫变换 霍夫(Hough)变换在检测各种形状的技术中非常流行&#xff0c;如果要检测的形状可以用数学表达式描述&#xff0c;就可以是使用霍夫变换检测它。即使要检测的形状存在一点破坏或者扭曲也是可以使…

第三章 图论 No.13拓扑排序

文章目录 裸题&#xff1a;1191. 家谱树差分约束拓扑排序&#xff1a;1192. 奖金集合拓扑序&#xff1a;164. 可达性统计差分约束拓扑序&#xff1a;456. 车站分级 拓扑序和DAG有向无环图联系在一起&#xff0c;通常用于最短/长路的线性求解 裸题&#xff1a;1191. 家谱树 119…

浅析3D打印技术

目录 1.3D打印的概念 2.3D打印的发展过程 3.3D打印的应用领域 4.3D打印带来的技术变革 1.3D打印的概念 3D打印是一种制造技术&#xff0c;它使用逐层堆叠材料的方式来创建物体。与传统的加工方法相比&#xff0c;3D打印具有很多优势。 在3D打印中&#xff0c;一种叫做CAD&am…

vue项目报错:node:internal/modules/cjs/loader:1080

运行项目报错&#xff1a; 原因&#xff1a; 看划线的地方&#xff0c;中文乱码导致找不见模块了 解决方案 将路径上的中文改为英文即可&#xff0c;项目命名最好只有英文、下划线&#xff08;_&#xff09;、数字、横杠&#xff08;-&#xff09;等英文符号组成

Mysql - 配置Mysql主从复制-keepalived高可用-读写分离集群

目录 高可用&#xff1a; 为什么需要高可用呢&#xff1f; 高可用的主要作用&#xff1a; keepalived是什么&#xff1f;它用在哪里&#xff1f; 什么是VRRP协议&#xff0c;它的作用是什么&#xff1f; 搭建一个基于keepalived的高可用Mysql主从复制读写分离集群 一、项…

C语言笔记7

#include <stdio.h> int main(void) {int a123;int b052;//十进制42int c0xa2;//十进制162printf("a%d b%o c%x \n",a,b,c);//分别是十进制 八进制 十六进制printf("a%d b%d c%d \n",a,b,c);printf("Hello 凌迟老头\n");return …

人工智能原理概述 - ChatGPT 背后的故事

大家好&#xff0c;我是比特桃。如果说 2023 年最火的事情是什么&#xff0c;毫无疑问就是由 ChatGPT 所引领的AI浪潮。今年无论是平日的各种媒体、工作中接触到的项目还是生活中大家讨论的热点&#xff0c;都离不开AI。其实对于互联网行业来说&#xff0c;自从深度学习出来后就…

“记账”很麻烦,看这场竞赛中的队伍与合合信息是如何解决问题的

在我们日常生活中或多或少都会有记账的情况&#xff0c;以此来对自己的收支和消费习惯进行分析&#xff0c;来帮助自己减少不必要的开支&#xff0c;优化财务决策、合理分配资金&#xff0c;减少财务压力和不必要的浪费。 但记账这个动作本身就是一件比较麻烦的。虽然现阶段有…

关于git执行提交报错问题

1.在执行git中 执行git init 执行 git add . 执行git commit -m “first commit” 时会出现 git config --global user.email "youexample.com" git config --global user.name "username" 这个问题是由于在git中没有设置默认的user跟emali导致需要手…

Ubuntu设置定时重启

1.安装/更新 cron 安装crontab sudo apt-get install cron更新命令 sudo apt-get update2.配置cron定时任务 sudo nano /etc/crontab* * * * * root reboot(从左到右&#xff0c;五个 * 依次是 分&#xff0c;时 &#xff0c;天&#xff0c;月&#xff0c;星期)下列命令表示…

高效反编译luac文件

对于游戏开发人员,有时候希望从一些游戏apk中反编译出源代码,进行学习,但是如果你触碰到法律边缘,那么你要非常小心。 这篇文章,我针对一些用lua写客户端或者服务器的编译过的luac文件进行反编译,获取其源代码的过程。 这里我不赘述如何反编译解压apk包的过程了,只说重点…

sealos安装k8s

一、前言 1、我前面文章有写过使用 kubeadm 安装的方式&#xff0c;大家可以去参考 &#xff08;二&#xff09;k8s集群安装&#xff0c;有一系列的k8s文章说明 2、安装k8s的方式有很多 kubeadmsealoskubespray等等 3、关于sealos来安装 k8s &#xff0c;也是非常建议大家去…

华为网络篇 RIPv2的基础配置-25

难度 1复杂度1 目录 一、实验原理 1.1 RIP的版本 1.2 RIP的路由更新方式 1.3 RIP的计时器 1.4 RIP的防环机制 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 RIP&#xff08;Routing Information Protocol&#xff0c;路由信息协议&#xff09;&am…

极狐GitLab 企业级 CI/CD 规模化落地实践指南(一)

目录 template 引用&#xff0c;减少代码冗余&#xff0c;增强 CI/CD 构建扩展性 问题 1&#xff1a;代码冗余&#xff0c;低效实践 问题 2&#xff1a;维护性难&#xff0c;工作量大 ➤ local ➤ file ➤ remote ➤ template 收益 1&#xff1a;一处修改&#xff0c;多…

leetcode810. 黑板异或游戏(博弈论 - java)

黑板异或游戏 lc 810 - 黑板异或游戏题目描述博弈论 动态规划 lc 810 - 黑板异或游戏 难度 - 困难 原题链接 - 黑板异或游戏 题目描述 黑板上写着一个非负整数数组 nums[i] 。 Alice 和 Bob 轮流从黑板上擦掉一个数字&#xff0c;Alice 先手。如果擦除一个数字后&#xff0c;剩…

grafana 的 ws websocket 连接不上的解决方式

使用了多层的代理方式&#xff0c;一层没有此问题 错误 WebSocket connection to ‘wss://ip地址/grafana01/api/live/ws’ failed: 日志报错 msg“Request Completed” methodGET path/api/live/ws status403 解决方式 # allowed_origins is a comma-separated list of o…

实时安全分析监控加强网络安全

网络犯罪分子只需几分钟&#xff0c;有时甚至几秒钟即可泄露敏感数据。但是&#xff0c;IT 团队可能无法在数周内发现这些违规行为。通常&#xff0c;这些违规行为是由外部方或客户发现的&#xff0c;到那时为时已晚。随着网络漏洞的激增&#xff0c;对安全分析的需求空前高涨。…

YOLOv5白皮书-第Y6周:模型改进

&#x1f4cc;本周任务&#xff1a;模型改进&#x1f4cc; 注&#xff1a;对yolov5l.yaml文件中的backbone模块和head模块进行改进。 任务结构图&#xff1a; YOLOv5s网络结构图: 原始模型代码&#xff1a; # YOLOv5 v6.0 backbone backbone:# [from, number, module, args]…