Kafka高性能的原因——零拷贝机制

3.2 零拷贝(Kafka高性能的原因之一)

“零拷贝”通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式。
Kafka所有数据的写如何读取都是通过零拷贝实现的。

优势
  • 减少甚至完全避免不必要的CPU拷贝,从而让CPU解脱出来去执行其他的任务
  • 减少内存带宽的占用
  • 通常零拷贝技术还能够减少用户空间和操作系统内核空间之间的上下文切换
传统I/O:四次拷贝与四次上下文切换

以将磁盘文件发送到网络为例,传统模式下系统调用如下:

read(file,tmp_buf,len)
write(socket,tmp_buf,len)
  • 程序使用read()系统调用。系统由用户态转换为内核态(第一次上下文切换),磁盘中的数据由DMA(Direct Memory Access)的方式读取到内核缓冲区(kernel buffer)。DMA过程中CPU不需要参与数据的读写,而是DMA处理器直接将硬盘数据通过总线传输到内存中。
  • 系统由内核态转换为用户态(第二次上下文切换),当程序要读取的数据已经完成写入内核缓冲区以后,程序会将数据由内核缓存区,写入用户缓存区),这个过程需要CPU参与数据的读写。
  • 程序使用write()系统调用。系统由用户态切换到内核态(第三次上下文切换),数据从用户态缓冲区写入到网络缓冲区(Socket Buffer),这个过程需要CPU参与数据的读写。
  • 系统由内核态切换到用户态(第四次上下文切换),网络缓冲区的数据通过DMA的方式传输到网卡的驱动(存储缓冲区)中(protocol engine)

传统的I/O方式会经过4次用户态和内核态的切换(上下文切换),两次CPU中内存中进行数据读写的过程。这种拷贝过程相对来说比较消耗资源。

内存映射方式I/O:两次拷贝与四次上下文切换
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

这是使用的系统调用方法,这种方式的I/O原理就是将用户缓冲区(user buffer)的内存地址和内核缓冲区(kernel buffer)的内存地址做一个映射,也就是说系统在用户态可以直接读取并操作内核空间的数据。

  1. mmap()系统调用首先会使用DMA的方式将磁盘数据读取到内核缓冲区,然后通过内存映射的方式,使用户缓冲区和内核读缓冲区的内存地址为同一内存地址,也就是说不需要CPU再讲数据从内核读缓冲区复制到用户缓冲区。
  2. 当使用write()系统调用的时候,cpu将内核缓冲区(等同于用户缓冲区)的数据直接写入到网络发送缓冲区(socket buffer),然后通过DMA的方式将数据传入到网卡驱动程序中准备发送。

可以看到这种内存映射的方式减少了CPU的读写次数,但是用户态到内核态的切换(上下文切换)依旧有四次,同时需要注意在进行这种内存映射的时候,有可能会出现并发线程操作同一块内存区域而导致的严重的数据不一致问题,所以需要进行合理的并发编程来解决这些问题。

零拷贝:两次拷贝与两次上下文切换
sendfile(socket, file, len);

CPU已经不参与数据的拷贝过程,也就是说完全通过其他硬件和中断的方式来实现数据的读写过程吗,但是这样的过程需要硬件的支持才能实现。

借助于硬件上的帮助,我们是可以办到的。之前我们是把页缓存的数据拷贝到socket缓存中,实际上,我们仅仅需要把缓冲区描述符传到socket缓冲区,再把数据长度传过去,这样DMA控制器直接将页缓存中的数据打包发送到网络中就可以了。

  1. 系统调用sendfile()发起后,磁盘数据通过DMA方式读取到内核缓冲区,内核缓冲区中的数据通过DMA聚合网络缓冲区,然后一齐发送到网卡中。

可以看到在这种模式下,是没有一次CPU进行数据拷贝的,所以就做到了真正意义上的零拷贝,这种模式实现起来需要硬件的支持。

发布了54 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/dong_W_/article/details/105327757