6

【一知半解】零值拷贝 - Hitechr

 2 years ago
source link: https://www.cnblogs.com/hitechr/p/16527805.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【一知半解】零值拷贝

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

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

数据在读写的过程中都需要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 (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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK