• 【一知半解】零值拷贝


    传统IO

    1. 应用调用read方法向操作系统发起读数据的请求,此时由用户态切换为内核态
    2. 当系统收到读数据请求时,利用DMA控制器把数据从磁盘读取到系统缓存区中(图中2.1)
    3. 再然后CPU会把系统缓存区的数据写应用缓存区(图2.2),此时由内核态切换为用户态
    4. 应用再调用write方法通知系统进行数据的写操作,此时由用户态切换为内核态
    5. CPU把应用缓存中的数据写到系统缓存区(图2.3)
    6. 再然后就是DMA控制器再把数据从系统缓存区写到网卡缓存区上(图2.4),write方法返回,此时由内核态切换为用户态

    在普通的IO拷贝时要有4次的上下文切换过程和4次的拷贝过程,在高并发的场景下对性能会产生比较大的影响。

    DMA

    数据在读写的过程中都需要CPU发出对应的命令来完成,因为CPU的速度比IO操作要快的多,在数据拷贝的过程中CPU不可能一直处于等待的过程,但是如果不等,CPU又不知道IO什么时候处理完,为了协调高速的CPU与低速的IO的矛盾,因此引入了DMA,DMA(Direct Memory Access)直接内存访问技术,通过它来进行内存和IO设备的数据传输。大至就是CPU给DMA发一个指令,然后DMA开始干活,然后CPU干别的活,DMA干完后告诉CPU,从而减少了CPU的等待时间。

    零值拷贝

    观察图可以2.1 和2.2 感觉数据在此过程做了个无用功,从内核态搬到用户态,再由用户态搬到内核态,因此提出了零值拷贝,零值拷贝不是没有拷贝,而是不再有用户态和内核态间数据拷贝过程,他的实现有mmapsendfile两种方式。

    mmap

    mmap (member map)内存映射,数据读取到系统缓存区后,用户态数据在系统缓存区的映射就可以完成数据的传输过程。

    1. 首先向操作系统发送一个mmap命令(上图1.1),此时由用户态切换为内核态
    2. 系统收到mmap命令后,会用DMA把数据从磁盘读到到内存的缓存区中(上图1.2)。
    3. 返回数据缓存区的一个映射mmap(上图1.3),由内核态切换到用户态
    4. 用户在发起一个write的操作(上图1.4),由用户态切换到内核态
    5. cpu再把内存缓存区中的数据拷贝到另一个缓存区上(上图1.5)
    6. DMA把数据拷贝到网卡上后返回(上图1.6),并由内核态切换到用户态

    mmap的操作共有4个上下文的切换和3次的数据拷贝过程,比普通的拷贝少了一次cpu的拷贝。

    sendfile

    mmap的拷贝方法要先获取数据区的映射,然后有用户发起写数据的命令后,在往网卡上写,如果不需要对数据做任何处理,只是原封不动的把数据发送出去,mmap命令和write命令可以合并为一个命令,直接告诉操作系统往外发送哪个数据即可,合并后的命令就sendfile。

    1. 用户往操作系统发直sendfile的命令,此时有用户态切换到内核态
    2. 系统收到命令后,由DMA把数据从磁盘上读取到系统缓存区上
    3. 然后由cpu把读到的到数据拷贝到另一个缓存区上
    4. 再由DMA把数据写到网上上
    5. 然后返回,由内核态切换到用户态

    sendfile的过程由2次上下文的切换和3次的数据拷贝过程,整个过程读到的数据对用户空间不可见,适应用静态文件服务器。

    sendfile升级

    在sendfile的cpu把数据从缓存区从一个地方拷贝到另一个地方,也会浪费额外的空间,能不能在往网卡上写数据时只用第一次DMA拷贝出来的数据,直接拷贝到网卡上呢?因此对sendfile再次升级,引入了DMA Scatter/Gather 分散/收集功能。

    1. 用户发起sendfile命令(上图1.1),系统收到后,由用户态 切换到内核态
    2. 调用DMA利用scatter从磁盘读取数据到缓存区离散存储(上图1.2)
    3. cpu把缓存区数据的文件描述符和数据长度发送给另一个缓存区(上图1.3)
    4. DMA在从另一个缓存区读取数据时根据文件描述符和数据长度,使用scatter/gather从缓存区中读取到网卡上(上图1.4)
    5. 返回,并有内核态切换到用户态

    此过程对用户空间仍然是不可见的,而且需要硬件的支持。有2次上下文切换和2次的拷贝过程,性能在大幅的提高。

    场景

    kafka和rocketmq都用到了mmap技术,两个中间件都有消息的持久化过程,和消息的读取过程。

    mq对消息持久化和读取的过程都用到了mmap+write,而kafka则是持久化的过程用到了mmap+write,读取消息用的sendfile。

    总结

    为什么需要DMA?

    DMA是为了解决高速CPU和低速IO操作的矛盾,cpu发起一个读取指令后,由dma接管数据的读取复制工作,cpu处理别的事件。

    传统IO与零值拷贝有比较

    名称 过程 上下文切换次数 拷贝次数
    传统IO 读取命令、从磁盘读、写到缓存、写到用户缓存区、写到缓存区,写到网卡 4 4
    mmap+write mmap命令,从磁盘读,写到缓存、返回mmap、发起write命令,从缓存读,写到另一缓存,写到网卡 4 3
    sendfile sendfile命令,从磁盘读,写缓存区,写到另一缓存区,写网卡 2 3
    sendfile升级版 sendfile命令,从磁盘读,写缓存区,根据文件描述符从缓存区读,写网卡 2 2


  • 相关阅读:
    动态规划解股票类型
    Docker简单使用
    【HTML——网页端“超级玛丽”】(游戏效果+代码)
    super和this有什么区别?-java
    2022-06-30 Unity核心8——模型导入
    【node进阶】在node.js中优雅的使用Socket.IO模块
    前端周刊第十四期
    python使用pandas中的read_csv函数读取csv数据为dataframe、查看dataframe数据中是否包含重复数据
    SQL教程之 开始学习用于数据分析的的五个SQL 命令 (教程含源码)
    捷报 | 美格智能Cat.1模组SLM332中标中国电信定制版Cat.1模组产品招募
  • 原文地址:https://www.cnblogs.com/hitechr/p/16527805.html