1.什么是零拷贝
零拷贝是指计算机在执行IO操作的时候, CPU不需要将数据从一个存储区复制到另一个存储区, 进而减少上下文切换以及 CPU 拷贝的时间, 这是一种
IO操作优化技术
零拷贝不是没有拷贝数据, 而是
减少用户态, 内核态的切换次数 和 CPU拷贝次数
2. 传统IO
3. 实现方式
3.1.mmap + write
虚拟内存把内核空间和用户空间的虚拟地址映射到同一个物理地址, 从而减少数据拷贝次数,
mmap
技术就是利用了虚拟内存的这个特点, 它将内核中的读缓冲区与用户空间的缓冲区进行映射, 所有的IO操作都在内核中完成
-
用户进程通过
mmap
方法向操作系统内核发起 IO 调用,上下文从用户态切换为内核态。 -
CPU 利用
DMA
控制器,把数据从硬盘中拷贝到内核缓冲区。 -
上下文从内核态切换回用户态,
mmap
方法返回。 -
用户进程通过write 方法向操作系统内核发起IO 调用,上下文从用户态切换为内核态。
-
CPU 将内核缓冲区的数据拷贝到的 socket 缓冲区。
-
CPU 利用
DMA
控制器,把数据从 socket 缓冲区拷贝到网卡,上下文从内核态切换回用户态,write 调用返回。
mmap+write
实现的零拷贝,I/O 发生了4次用户空间与内核空间的上下文切 换,以及3 次数据拷贝(包括了2次DMA
拷贝和1 次CPU 拷贝)。
3.2 sendfile
sendfile
表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的, 避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作
-
用户进程发起·
sendfile
系统调用,上下文(切换 1)从用户态转向内核态 -
DMA
控制器,把数据从硬盘中拷贝到内核缓冲区。 -
CPU
将读缓冲区中数据拷贝到 socket 缓冲区 -
DMA
控制器,异步把数据从 socket 缓冲区拷贝到网卡, -
上下文(切换2)从内核态切换回用户态,
sendfile
调用返回。
2次上下文切换,2次DMA
拷贝,一次cpu
拷贝
3.3 带DMA
收集功能sendfile
(sendfile+DMA Scatter/Gather
)
linux 2.4
版本之后,对sendfile
做了优化升级,引入SG-DMA
技术,其实就是对DMA
拷贝加入了scatter/gather
操作,它可以直接从内核空间缓冲区中将数据读取到网卡。使用这个特点搞零拷贝,即还可以多省去一次CPU拷贝。
-
用户进程发起
sendfile
系统调用,上下文(切换1)从用户态转向内核态 -
DMA
控制器,把数据从硬盘中拷贝到内核缓冲区。 -
CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)发送到socket缓冲区
-
DMA
控制器根据文件描述符信息,直接把数据从内核缓冲区拷贝到网卡 -
上下文(切换2)从内核态切换回用户态,
sendfile
调用返回。
可以发现,sendfile+DMA scatter/gather
实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及2次数据拷贝。其中2次数据拷贝都是包DMA
拷贝。这就是真正的 零拷贝(Zero-copy) 技术,全程都没有通过CPU来搬运数据,所有的数据都是通过DMA
来进行传输的。
4. DMA
技术说明
DMA
,英文全称是Direct Memory Access,即直接内存访问。DMA
本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU的参与。
5.应用场景
RocketMQ
和Kafka
都使用到了零拷贝的技术。
对于MQ
而言,无非就是生产者发送数据到MQ
然后持久化到磁盘,之后消费者从MQ
读取数据。
对于RocketMQ
来说这两个步骤使用的是mmap+write
,而Kafka则是使用mmap+write
持久化数据,发送数据使用sendfile
6.java
对零拷贝支持
6.1 Java NIO
对mmap
的支持
MappedByteBuffer
6.2 Java NIO
对sendfile
的支持
FileChannel
的transferTo()/transferFrom()
,底层就是sendfile()
系统调用函数