一、零拷贝技术的背景
在传统的数据传输过程中,当需要将磁盘中的数据发送到远程服务器时,数据通常需要经过多次拷贝和上下文切换。具体来说,这些步骤包括:
四次拷贝
- 从硬盘到内核缓冲区:
- 当用户进程通过read()系统调用请求读取数据时,CPU会发起指令,利用DMA(Direct Memory Access,直接内存访问)控制器将数据从硬盘中拷贝到内核的读缓冲区(Page Cache)中。这一步骤是DMA完成的,不需要CPU的直接参与,但算作一次数据拷贝。
- 从内核缓冲区到用户缓冲区:
- 数据到达内核缓冲区后,CPU会介入,将数据从内核缓冲区拷贝到用户进程的缓冲区中。这是第二次数据拷贝,发生在内核态和用户态之间。
- 从用户缓冲区到内核Socket缓冲区:
- 当用户进程准备将数据发送出去时,它会通过write()系统调用将数据从用户缓冲区拷贝到内核的网络Socket缓冲区中。这是第三次数据拷贝,同样发生在内核态和用户态之间。
- 从内核Socket缓冲区到网卡:
- 最后,CPU会再次利用DMA控制器,将Socket缓冲区中的数据拷贝到网卡,以便通过网络发送。这是第四次数据拷贝,也是由DMA完成的,但算作一次拷贝操作。
四次上下文切换
在四次拷贝的过程中,还伴随着四次上下文切换:
- read()系统调用时:
- 用户进程从用户态切换到内核态,以便CPU能够处理read()请求。这一步用户请求是阻塞的
- read()系统调用完成后:
- CPU将数据从硬盘拷贝到内核缓冲区后,上下文从内核态切换回用户态,read()调用返回。
- write()系统调用时:
- 用户进程再次从用户态切换到内核态,以便CPU能够处理write()请求。
- write()系统调用完成后:
- CPU将数据从Socket缓冲区拷贝到网卡后,上下文从内核态切换回用户态,write()调用返回。
- 从磁盘读取数据到内核缓冲区。
- 将内核缓冲区中的数据拷贝到用户空间缓冲区。
- 应用程序通过write()方法将用户空间缓冲区中的数据拷贝到内核空间的Socket Buffer。
- 将Socket Buffer中的数据拷贝到网卡缓冲区(NIC Buffer)。
- 网卡缓冲区将数据传输到目标服务器。
图中的四个方框标出来的是这个过程中的四次拷贝,四个椭圆标记出来的是4次上下文切换
在这个过程中,数据被拷贝了四次,并且存在两次不必要的拷贝(从内核空间到用户空间的拷贝),这导致了CPU资源的浪费和性能的下降。
二、Kafka中的零拷贝实现
Kafka在设计和实现时充分利用了零拷贝技术,以提高数据传输的效率和系统的吞吐量。Kafka中的零拷贝主要通过sendfile系统调用来实现,这个过程在Linux系统中尤为高效。
- sendfile系统调用:
- 当Kafka需要发送数据时,它会使用sendfile系统调用,该调用允许数据直接从文件描述符(磁盘文件)传输到另一个文件描述符(通常是套接字),而无需将数据读入用户空间缓冲区。
- sendfile通过DMA(Direct Memory Access)技术,将数据直接从磁盘读取到内核的读取缓冲区,然后通过DMA引擎将数据从内核缓冲区传输到网卡设备,而无需经过用户空间。
- 减少拷贝次数和上下文切换:
- 在使用零拷贝的情况下,数据只经历两次拷贝:一次是从磁盘到内核缓冲区,另一次是从内核缓冲区到网卡缓冲区。
- 同时,由于减少了用户空间和内核空间之间的切换,CPU的上下文切换次数也显著减少,这进一步提高了性能。
- FileChannel.transferTo()方法:
- 在Java中,Kafka可能通过FileChannel.transferTo()方法的底层实现来利用sendfile系统调用。该方法允许直接将字节从文件通道传输到另一个可写字节通道,而无需将数据读入用户空间缓冲区。
-
Mmap技术
前面三条说的都是sendFile相关的,Kafka还用到了另外一种零拷贝技术MMapmmap是一种内存映射文件的方法,它可以将文件映射到进程的地址空间中,使进程能够直接访问和操作文件内容,而无需进行显式的拷贝操作。这种技术对于提高文件访问速度和减少内存使用非常有效。-
在Kafka服务端(Broker),mmap主要用于读操作。当客户端(这里是Consumer)向Kafka服务端发送请求时,服务端会利用mmap技术直接读取请求数据,并将其内容发送给消费者。这种读取方式避免了不必要的内存拷贝,从而提高了数据传输效率。此外,mmap还可以减少内存的使用,因为它只映射文件的一部分到内存中,而不是将整个文件加载到内存中。
-
Mmap是一种内存映射文件的方法,它可以将文件映射到进程的地址空间中,使进程能够直接访问和操作文件内容,而无需进行显式的拷贝操作。这种技术对于提高文件访问速度和减少内存使用非常有效。
-
三、零拷贝的优势
- 提高数据传输效率:通过减少数据拷贝次数和CPU上下文切换,零拷贝技术显著提高了数据传输的效率。
- 降低延迟:减少了数据传输的中间环节,降低了数据传输的延迟。
- 提高吞吐量:优化了数据传输过程,使得Kafka能够处理更多的并发请求,提高了系统的吞吐量。
- 减少资源消耗:降低了CPU和内存的资源消耗,提高了系统的整体性能。