zoukankan      html  css  js  c++  java
  • 什么是零拷贝

     1、名词介绍

      内核:操作系统的核心

      用户空间:指的是用户进程的运行空间

      内核空间:指的是内核的运行空间

      用户态:如果进程运行在用户空间就是用户态

      内核态:如果进程运行在内核空间就是内核态

      DMA拷贝:

        对一个IO操作而言,是通过CPU发出对应的指令来完成的,但是相比CPU来说,IO的速度太慢了,CPU有大量的时间处于等待IO状态。

        因此就产生了DMA(Direct Memory Access):直接内存访问技术。是一种无需CPU的参与就可以让IO设备与系统内存之间进行双向数据传输机制。

        使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来,从而大大提高系统的吞吐率。DMA方式的数据传输由DMA控制器(DMAC)控制,在传输期间,CPU可以并发的执行其他任务。当DMA结束后,DMAC通过中断通知CPU数据传输已经结束,由CPU执行相应的中断服务程序进行后续处理。

        本质上说DMA控制器就是主板上一块独立的芯片,通过它来进行内存和IO设备的数据传输,从而减少CPU的等待时间。

       

      上下文切换:内核在CPU上对进程或者线程进行切换。基本原理是,当发生任务切换时,保存当前任务的寄存器到进程控制块(PCB,Process Control Block)中,将下一个即将要切换过来的任务的寄存器状态恢复到当前CPU寄存器中,使其继续执行,同一时刻只允许一个任务独享寄存器。

        上下文的切换流程如下 :

        (1)挂起一个进程,将这个进程在CPU中的状态(上下文信息)存储于内存的PCB中。 
        (2)在PCB中检索下一个进程的上下文并将其在CPU的寄存器中恢复。 
        (3)跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行)并恢复该进程

      从Linux系统上看,除了引导系统的BIN区,整个内存空间主要分为两个部分:内核空间、用户空间

        内核空间是Linux自身使用的内存空间,主要提供给程序调度、内存分配、连接硬件等程序逻辑使用;

        用户空间则是提供给各个进程的主要空间。

        用户空间不具有访问内核空间资源的权限,因此如果应用程序需要使用到内核空间的资源,则需要通过系统调用来完成(也就是调用操作系统内核提供的API):进行上下文切换,从用户空间切换到内核空间,然后在完成相关操作后再从内核空间切换回用户空间。

      当程序请求网络数据的时候,需要经历两次拷贝:

        ①:程序需要等待数据从网卡拷贝到内核空间

        ②:因为用户程序无法访问内核空间,所以内核又得把数据拷贝到用户空间,这样处于用户空间的程序才能访问这个数据

     2、什么是零拷贝

      零拷贝技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。

      简单来说就是减少和避免不必要的CPU数据拷贝过程,从而减少拷贝的CPU的开销及用户态和内核态之间的切换次数,从而优化数据传输的性能。

      这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽

      几种零拷贝实现思路:

        1)直接IO:数据直接跨过内核,在用户空间和IO设备之间传递,内核只是进行必要的虚拟存储配置等辅助工作

        2)避免内核和用户空间之间的数据拷贝:当用户空间不需要对数据进行访问时,则可以避免将数据从内核空间拷贝到用户空间(如sendfile方式)

        3)写时复制:数据不需要提前拷贝,而是当需要修改的时候再进行部分拷贝

      1)传统IO方式

        传统的IO方式,底层实际上通过调用read()和write()来实现。通过read() 把数据从硬盘读取到内核缓冲区,再复制到用户缓冲区;然后再通过write()写入到Socket缓冲区,最后写入网卡设备。

        

        DMA Copy不需要CPU全程参与,CPU Copy需要CPU全程参与。

        读操作:

          ①:用户进程通过read()方法向操作系统发起调用;此时上下文从用户态切换为内核态

          ②:DMA控制器把数据从硬盘中拷贝到内核的读缓存区,发生一次DMA Copy

          ③:CPU把内核读缓存区数据拷贝到应用缓冲区,发生了一次CPU Copy

          ④:read调用返回后,上下文再次从内核态切换为用户态

          发生两次上下文切换,两次Copy

        写操作:

          ①:用户进程通过write()方法发起调用,上下文从用户态切换为内核态 

          ②:CPU将应用缓冲区的数据Copy到内核的Socket缓冲区

          ③:DMA控制器把数据从Socket缓冲区Copy到网卡(或其他存储设备)

          ④:写入结束后返回,上下文从内核态切换回用户态

          发生两次上下文切换,两次Copy

        传统的IO读写操作,总共进行了4次上下文切换,4次Copy动作。数据在内核空间和应用空间之间来回复制,严重消耗资源。

        4次Copy如下:

          ①:读取磁盘文件到操作系统内核缓冲区(DMA Copy)

          ②:将内核缓冲区的数据Copy到应用程序缓冲区(CPU Copy)

          ③:将应用程序缓冲区的数据,Copy到内核Socket网络发送缓冲区(CPU Copy)

          ④:将内核Socket缓冲区的数据Copy到网卡(DMA Copy)

        注意:为了程序和系统安全考虑,操作系统为每个应用程序都设计了用户内存,且用户内存和内核内存是隔离的。这也是需要从应用程序缓冲区到内核缓冲区来回复制的原因了。

      

        虚拟内存技术

        虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行交换。

        背景:电脑中所运行的程序均需由经过内存执行,若执行的程序占用内存很大或很多,则会导致内存消耗殆尽。为解决该问题,window中运用了虚拟内存技术(linux中成为交换空间),即匀出一部分硬盘空间来充当内存使用。当内存耗尽时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。

        所有现代操作系统都使用虚拟内存,使用虚拟地址取代物理地址,这样做的好处就是:

          1、多个虚拟内存可以指向同一个物理地址

          2、虚拟内存空间可以远远大于物理内存空间

       使用虚拟内存技术将内核空间和用户空间的虚拟地址映射到同一个物理地址,这样就不用在程序和内核之间来回复制。传统的IO操作经过虚拟地址优化后就省去了上面的CPU copy:

       

       2) mmap+write方式

        mmap:一种内存映射文件的方法,将一个或者其他对象映射进内存

        使用mmap+write方式替换原来的传统IO方式,就是利用了虚拟内存的特性,mmap+write的IO读写过程:

        

         整体流程的核心区别就是,把数据读取到内核缓冲区后,应用程序进行写入操作时,直接把内核的Read Buffer的数据复制到Socket Buffer以便进行写入,这次内核之间的复制也是需要CPU参与的。

        与传统的IO流程相比,少了一次CPU Copy,不过上下文切换还是4次(4次上下文切换,3次Copy

        mmap+write方式减少了CPU的使用,适用于大文件的传输。

        

       3)sendfile方式

        相比mmap来说,sendfile同样减少了一次CPU Copy,而且减少了2次上下文切换。

        sendfile是Linux2.1内核版本后引入的一个系统调用函数,通过使用sendfile数据可以直接在内核空间进行传输,因此避免了用户空间和内核空间的拷贝,流程如下:

        

        整个过程发生了2次用户态和内核态的上下文切换和3次拷贝,具体流程如下:

         1)用户进程通过sendfile()方法向操作系统发起调用,上下文从用户态转向内核态
         2)DMA控制器把数据从硬盘中拷贝到读缓冲区
         3)CPU将读缓冲区中数据拷贝到socket缓冲区
         4)DMA控制器把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,sendfile调用返回


        sendfile方法IO数据对用户空间完全不可见,所以只能适用于完全不需要用户空间处理的情况,比如静态文件服务器

        

      应用:

      RocketMQ和Kafka都使用了零拷贝的技术

      对于MQ,整个流程为:生产者发送数据到MQ,然后持久化到磁盘,之后消费者从磁盘读取数据。

      对于RocketMQ来说这两个步骤使用的是mmap+write,而Kafka则是使用mmap+write持久化数据,发送数据使用sendfile

        

      总结:

      由于CPU和IO速度的差异问题,产生了DMA技术,通过DMA搬运来减少CPU的等待时间。

      传统的IO read+write方式会产生2次DMA拷贝+2次CPU拷贝,同时有4次上下文切换。

      而通过mmap+write方式则产生2次DMA拷贝+1次CPU拷贝,4次上下文切换,通过内存映射减少了一次CPU拷贝,可以减少内存使用,适合大文件的传输。

      sendfile方式是新增的一个系统调用函数,产生2次DMA拷贝+1次CPU拷贝,但是只有2次上下文切换。因为只有一次调用,减少了上下文的切换,但是用户空间对IO数据不可见,适用于静态文件服务器

      

      参考:

      https://mp.weixin.qq.com/s/yvenw3P2JvvSxWodBNNcMw

      https://www.jianshu.com/p/e76e3580e356

      https://blog.51cto.com/12182612/2424692?source=dra

    END.

  • 相关阅读:
    [LeetCode] Container With Most Water
    [LeetCode] Sort List 排序 sort
    c/c++: c++函数返回类型什么情况带const
    [LeetCode] Restore IP Addresses 回溯
    [LeetCode] Binary Tree Inorder Traversal 中序排序
    [LeetCode] Evaluate Reverse Polish Notation stack 栈
    [acmm week12]染色(容斥定理+组合数+逆元)
    [acmm week12]二分+dp+单调队列
    [csp-201809-3]元素选择器-编译原理
    [csp-201709-3]JSON查询-编译原理
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/14576216.html
Copyright © 2011-2022 走看看