1.什么是零拷贝

零拷贝是指计算机在执行IO操作的时候, CPU不需要将数据从一个存储区复制到另一个存储区, 进而减少上下文切换以及 CPU 拷贝的时间, 这是一种IO操作优化技术

零拷贝不是没有拷贝数据, 而是减少用户态, 内核态的切换次数 和 CPU拷贝次数

2. 传统IO

零拷贝的原理 java 零拷贝 mmap_数据

3. 实现方式

3.1.mmap + write

虚拟内存把内核空间和用户空间的虚拟地址映射到同一个物理地址, 从而减少数据拷贝次数, mmap技术就是利用了虚拟内存的这个特点, 它将内核中的读缓冲区与用户空间的缓冲区进行映射, 所有的IO操作都在内核中完成

零拷贝的原理 java 零拷贝 mmap_零拷贝的原理 java_02

  1. 用户进程通过mmap方法向操作系统内核发起 IO 调用,上下文从用户态切换为内核态。
  2. CPU 利用DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
  3. 上下文从内核态切换回用户态,mmap 方法返回。
  4. 用户进程通过write 方法向操作系统内核发起IO 调用,上下文从用户态切换为内核态。
  5. CPU 将内核缓冲区的数据拷贝到的 socket 缓冲区。
  6. CPU 利用DMA 控制器,把数据从 socket 缓冲区拷贝到网卡,上下文从内核态切换回用户态,write 调用返回。

mmap+write换,以及3 次数据拷贝(包括了2次DMA拷贝和1 次CPU 拷贝)。

3.2 sendfile

sendfile避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作

零拷贝的原理 java 零拷贝 mmap_零拷贝的原理 java_03

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

DMA拷贝,一次cpu 拷贝

3.3 带DMA收集功能sendfilesendfile+DMA Scatter/Gather)

linux 2.4版本之后,对sendfile做了优化升级,引入SG-DMA技术,其实就是对DMA拷贝加入了scatter/gather操作,它可以直接从内核空间缓冲区中将数据读取到网卡。使用这个特点搞零拷贝,即还可以多省去一次CPU拷贝

零拷贝的原理 java 零拷贝 mmap_用户态_04

  1. 用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态
  2. DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
  3. CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)发送到socket缓冲区
  4. DMA控制器根据文件描述符信息,直接把数据从内核缓冲区拷贝到网卡
  5. 上下文(切换2)从内核态切换回用户态sendfile调用返回。

sendfile+DMA scatter/gather实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及2次数据拷贝。其中2次数据拷贝都是包DMA拷贝。这就是真正的 零拷贝(Zero-copy)DMA来进行传输的。

4. DMA 技术说明

DMA,英文全称是Direct Memory Access,即直接内存访问。DMA本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU的参与

零拷贝的原理 java 零拷贝 mmap_用户态_05

5.应用场景

RocketMQKafka都使用到了零拷贝的技术。

对于MQ而言,无非就是生产者发送数据到MQ然后持久化到磁盘,之后消费者从MQ读取数据。

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

6.java对零拷贝支持

6.1 Java NIOmmap的支持

MappedByteBuffer

6.2 Java NIOsendfile的支持

FileChanneltransferTo()/transferFrom(),底层就是sendfile() 系统调用函数