h

hitechr

V1

2022/07/28阅读:15主题:极客黑

【一知半解】零值拷贝

传统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

分类:

后端

标签:

Java

作者介绍

h
hitechr
V1