0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

传统IO演化至零拷贝的过程

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-09 09:09 次阅读

零拷贝(Zero-Copy)用于在数据读写过程中减少不需要的CPU拷贝,CPU就那么几个,减少它的负担自然可以提高处理效率。数据传输有本地的文件拷贝和通过socket进行文件传输两种,两者区别不大,只是文件数据最终的去向仍然是本地磁盘还是网卡的区别,这里以socket文件为例介绍传统IO演化至零拷贝的过程。

介绍零拷贝之前,可以先看一下传统IO,借此熟悉一些相关概念,先上图:

图片

首先要知道操作系统已经隔离了两块运行空间,即用户空间和内核空间。可以理解为用户程序是跑在用户空间的,而操作系统的内核代码是跑在内核空间的,把这两个隔离是为了用户程序的故障不影响操作系统。其实现代操作系统已经对数据的拷贝做了优化,之前把数据从底层硬件拷贝到内核空间也是CPU来的,现在CPU只需要通知一下DMA(Direct Memory Access,直接内存存取),拷贝工作就交给DMA了,这样CPU就解放出来做其他事去了,所以现代操作系统底层硬件和内核空间之间的数据拷贝CPU参与的很少可以不予考虑,都是DMA来的,但是内核空间和用户空间之间的活都是CPU亲自上的。

从上图可以看出,传统IO是这么几个步骤:

1.线程在用户空间发起read()读文件,线程从用户态切换为内核态

2.DMA将磁盘数据拷贝到内核缓存后,CPU又将数据从内核缓存拷贝至用户缓存,这时线程又从内核态切换为用户态

3.这时候知道了数据应该往哪里写,CPU将数据从用户缓存拷贝至socket缓存,线程又从用户态切换到内核态

4.最后DMA将数据从内核缓存拷贝到网卡,read()调用结束返回,线程又从内核态切换到用户态

整个过程线程上下文切换了四次,一共有四次拷贝,2次CPU来的,2次DMA来的。观察图不经会想,为啥数据要在用户空间走一趟呢,能不能在内核空间直接从内核缓存到socket缓存呢,答案是可以的,这就是第一种零拷贝技术的原理,即mmap+write,先上图:

图片

mmap即内存映射,mmap()是由unix/linux操作系统来调用的,它可以将内核缓存中的一块区域与用户缓存中的一块区域形成映射关系,即共享内存,不过在用户缓存中的这块映射区域是堆外内存。建立映射关系后,理解起来就是往其中任意一头写另外一头也写进去了,这样是为了省掉一次CPU拷贝,传统IO要把数据从内核缓存拷贝到用户缓存才能写,现在直接在用户缓存写,有了映射关系,对应的那块内核缓存也有了。mmap+write实现的零拷贝流程是这样的:

1.用户进程要读一个磁盘文件,告诉内核进程发起mmap()函数调用,来来来把你的内核缓存和我的一块用户缓存建立下映射关系,我要读这个磁盘文件了。

2.内核进程乖乖调用了mmap()函数,将一块内核缓存和用户缓存中的一块堆外内存建立的映射关系。并且告诉DMA将这个文件中的数据拷贝到了这块内核缓存中。到这里mmap()函数就调用结束了,任务完成。严格的说到这里为止都不算IO过程,因此也没有统计线程的上下文切换次数。

3.这才开始IO,因为磁盘文件已经被DMA拷贝到内核缓存中去了,又被映射到了这块堆外内存,所以就直接在用户缓存里就读到了,线程没有上下文切换,然后准备写进一块socket缓存里去了,线程发起了write()调用,状态由用户态切换为内核态,这时候内核基于CPU拷贝将数据从那块映射着的内核缓存拷贝到socket缓存,CPU也就拷贝了这一次。

4.然后又是DMA将数据从socket缓存拷贝到网卡,最后write()函数调用返回,线程从内核态切换到用户态。

整个过程线程切换了两次,一共有三次拷贝,其中2次DMA拷贝,1次CPU拷贝。到这里CPU已经轻松不少了,就拷贝了一次嘛,可以不是说好的零拷贝的嘛,怎么还有一次拷贝,然后sendfile()函数就登场了,它是实实在在的实现了零拷贝,先上图:

图片

sendfile()也是操作系统来调用的,用户线程只能通过特定的方法发起调用,比如java.nio包下的FileChannel,它的transferTo()方法可以发起sendfile()函数的调用。sendfile()函数实现零拷贝的过程是这样的:

1.用户线程发起sendfile()函数调用,与mmap()函数不同的是,不单单告诉内核去哪里读数据,往哪里写数据也一起告诉内核了。这时候就已经开始算IO了,线程从用户态切换到了内核态。

2.知道了从哪里读数据,依然是DMA去磁盘里把数据拷贝到内核缓存中去,由于同时也知道了应该往哪里写数据,那就接着干活呗。

3.先把数据描述信息从内核缓存复制到指定的socket缓存,然后DMA又来了,这个时候socket缓存中的数据描述信息就起作用了,这些描述信息主要是数据的位置信息等。DMA Gather通过这些数据描述信息将数据从内核缓存拷贝到网卡。

4.sendfile()函数调用结束,线程从内核态切换到了用户态,CPU一次拷贝都没有!零!

这就是真正的零拷贝,整个过程用户线程切换了两次,只有两次拷贝,但都是DMA来的。

关于第三种零拷贝方式,这是Linux2.4对sendfile做了改进之后的零拷贝。其实linux 2.1 内核开始就引入了sendfile()函数,当时的零拷贝是这样的。

图片

可以看出整个过程用户线程切换了两次,有三次拷贝,两次DMA来的,还是有一次CPU拷贝。这种零拷贝方式和mmap+write方式有点类似,但是这也算零拷贝演进过程中的一环。

sendfile()函数的man page里面有这句话: In Linux kernels before 2.6.33, out_fd must refer to a socket. Since Linux 2.6.33 it can be any file. 也就是说Linux2.6.33之前sendfile()只能用于文件到socket的传输。而Linux2.6.33之后可以用于两个文件描述符之间和文件到socket之间的传输。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • IO
    IO
    +关注

    关注

    0

    文章

    397

    浏览量

    38516
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10451

    浏览量

    206582
  • 数据
    +关注

    关注

    8

    文章

    6514

    浏览量

    87609
收藏 人收藏

    评论

    相关推荐

    linux下的IO模型详解

    同步的呢?因为无论以上哪种模型,真正的数据拷贝过程都是同步的(自己的理解便是:所有的数据拷贝过程都是用户进程手动执行的) 分享一个Linux IO
    发表于 10-09 16:12

    利用PLD实现智能演化计算

    演化计算是一种通过模拟的自然界的生物演化过程搜索最优解的方法,主要包括遗传算法(CA)、演化策略(ES)、演化规划(EP)等。演化计算具有子
    发表于 06-26 17:43 24次下载

    光盘拷贝机的使用端口

    光盘拷贝机的使用端口              使用端口指的是:光盘拷贝机内部控制器与刻录机相连的接口。目前光盘拷贝
    发表于 12-30 10:04 925次阅读

    用于时钟的动态演化模型及算法

    传统分布仿真系统时钟不一致影响因素分析方法,已不能满足当前面向服务分布仿真的时钟状态分析需要。从系统全局时钟演化出发,阐述了时钟状态演化内涵与过程;在此基础上,基于有限自动机理论,提出
    发表于 11-22 10:34 9次下载
    用于时钟的动态<b class='flag-5'>演化</b>模型及算法

    C#浅拷贝与深拷贝区别解析

     所谓浅拷贝就是将对象中的所有字段复制到新的副本对象中;浅拷贝对于值类型与引用类型的方式有区别,值类型字段的值被复制到副本中后,在副本中的修改不会影响源对象对应的值;然而对于引用类型的字段被复
    发表于 11-29 08:32 2.5w次阅读
    C#浅<b class='flag-5'>拷贝</b>与深<b class='flag-5'>拷贝</b>区别解析

    基于微博文本的词对主题演化模型

    针对传统主题模型忽略了微博短文本和文本动态演化的问题,提出了基于微博文本的词对主题演化( BToT)模型,并根据所提模型对数据集进行主题演化分析。BToT模型在文本生成
    发表于 12-03 11:31 14次下载
    基于微博文本的词对主题<b class='flag-5'>演化</b>模型

    演化数据的软件缺陷预测性能

    命周期内的演化本质上是一个物种的逐步进化,其缺陷的表现也必然带着该物种的特征,而且还受到进化历史中的演化轨迹的影响.已有一些研究人员开始研究软件演化过程,并提出了一些演化度量元.研究和
    发表于 01-05 11:42 0次下载

    Python如何防止数据被修改Python中的深拷贝与浅拷贝的问题说明

    在平时工作中,经常涉及到数据的传递。在数据传递使用过程中,可能会发生数据被修改的问题。为了防止数据被修改,就需要再传递一个副本,即使副本被修改,也不会影响原数据的使用。为了生成这个副本,就产生了拷贝——今天就说一下Python中的深拷贝
    的头像 发表于 03-30 09:54 2800次阅读
    Python如何防止数据被修改Python中的深<b class='flag-5'>拷贝</b>与浅<b class='flag-5'>拷贝</b>的问题说明

    激光增材制造过程中微结构及其演化

    激光增材制造过程中微结构及其演化与制造参数之间关联的计算预测,已成为基于增材制造的材料/结构设计和开发过程的重要组成部分。
    的头像 发表于 11-13 17:22 5006次阅读
    激光增材制造<b class='flag-5'>过程</b>中微结构及其<b class='flag-5'>演化</b>

    STM32CubeMX新建工程+ 基本IO配置过程

    STM32CubeMX新建工程+基本IO配置过程
    的头像 发表于 03-25 10:47 3543次阅读
    STM32CubeMX新建工程+ 基本<b class='flag-5'>IO</b>配置<b class='flag-5'>过程</b>

    相场模拟—尽“显”增材制造过程中的晶粒演化

    (powder-bed-fusion, PBF)技术是最常用的AM技术之一。理解并预测PBF过程中晶粒演化对通过调整工艺以定制样件的晶粒结构具有重要的指导意义。 目前,通过数值模拟方法(如元胞自动机法、相场法)可以很好地模拟PBF过程
    的头像 发表于 06-15 15:06 1661次阅读

    深入理解Linux传统的System Call I/O

    的数据读写流程,整个过程涉及 2 次 CPU 拷贝、2 次 DMA 拷贝,总共 4 次拷贝,以及 4 次上下文切换。 CPU 拷贝: 由 C
    的头像 发表于 11-19 09:52 1821次阅读
    深入理解Linux<b class='flag-5'>传统</b>的System Call I/O

    C++深拷贝和浅拷贝详解

    当类的函数成员存在指针成员时会产生深拷贝和浅拷贝和问题。
    发表于 08-21 15:05 184次阅读
    C++深<b class='flag-5'>拷贝</b>和浅<b class='flag-5'>拷贝</b>详解

    信号驱动IO与异步IO的区别

    , 是开始处理IO, 这个时候还是存在阻塞的,将数据从内核态拷贝进入到用户态的过程至少是阻塞住的 (应用程序将数据从内核态拷贝到用户态的过程
    的头像 发表于 11-08 15:32 402次阅读
    信号驱动<b class='flag-5'>IO</b>与异步<b class='flag-5'>IO</b>的区别

    什么是零拷贝技术

    传统操作系统的数据传输过程中,系统内部会在磁盘、内存、缓存中多次进行数据拷贝,每次都会占用CPU的资源,数据量小的时候还好。 随着数据量的增加,CPU的开销也会持续增加,尤其是在机器人图像数据
    的头像 发表于 11-27 16:20 239次阅读
    什么是零<b class='flag-5'>拷贝</b>技术