聊聊零拷贝技术原理和应用

0. 引言

最近看到一些关于零拷贝技术的帖子,写的比较零散,今天抽时间整理以下,系统的理解一下。

零拷贝技术是一种在数据传输过程中避免不必要的数据复制的优化手段。在计算机系统中,数据通常需要在不同的存储区域之间移动,例如从文件系统到应用程序的内存空间。零拷贝技术通过减少或消除这些数据复制操作,提高了性能和减少了资源消耗。

其实零拷贝技术我们可能一直在间接使用着。比如我们常见的一些中间件和优秀JAVA框架也使用零拷贝技术。

  1. 比如Netty 就支持通过使用零拷贝技术,例如 FileRegion 和 CompositeByteBuf,来减少数据传输过程中的数据复制。FileRegion 允许通过直接从文件系统发送数据到网络,避免了数据在用户空间和内核空间之间的复制。CompositeByteBuf 允许将多个 ByteBuf 合并成一个逻辑缓冲区,而不需要实际复制数据。

  2. 大名鼎鼎的Kafka 使用零拷贝技术,通过 sendfile 系统调用,将数据直接从磁盘发送到网络,从而避免了不必要的数据复制。可以参考这篇文章《Zero Copy. One Of Reason Behind Why Kafka So Fast.》
    在这里插入图片描述

  3. Nginx 它使用零拷贝技术主要通过以下两种方式实现

  1. sendfile系统调用 当Nginx需要将文件数据发送到网络时,它可以使用sendfile系统调用。sendfile允许数据直接从文件系统传输到网络,无需先将数据复制到应用程序的内存中。这不仅减少了数据复制的开销,还避免了CPU上下文切换。

  2. splice系统调用:对于代理或负载均衡等场景,Nginx可以使用splice系统调用来实现零拷贝数据转发。splice允许数据在两个文件描述符之间移动,无需先将数据复制到用户空间。这可以显著提高数据转发的效率。

这些中间件通过使用零拷贝技术,有效地提高了数据传输性能、降低了 CPU 使用率、减少了内存消耗,从而为用户提供了更好的性能和服务。

1. 什么是零拷贝技术

零拷贝技术是一种优化数据传输过程的技术,它通过避免数据在内核空间和用户空间之间的多次拷贝,直接将数据从输入/输出设备传输到应用程序的内存空间,提高数据传输效率和性能。传统的拷贝方式需要数据从内核缓冲区拷贝到用户缓冲区,而零拷贝技术在传输过程中避免了这一步骤。

在这里插入图片描述

1. 零拷贝技术在不同领域的应用

零拷贝技术(Zero-copy)主要被用于计算机网络和硬件设备检测,尤其是在网络通信、大数据处理、文件系统等领域有广泛的应用。

  1. 文件系统 在一些高性能的文件系统中,例如Linux的sendfile系统调用,可以直接将数据从文件系统的page cache发送到网络协议栈,无需在用户空间和内核空间之间复制数据。

  2. 网络通信 在网络应用中,零拷贝技术可以减少CPU的使用率,提高网络吞吐量。例如,在使用TCP/IP协议栈的网络通信中,通过使用DMA(Direct Memory Access)技术,可以直接将网络数据传输到内存,无需CPU参与。

  3. 大数据处理 在大数据处理中,零拷贝技术可以减少数据在内存和磁盘之间的复制,提高数据处理效率。例如,Apache Arrow是一种大数据处理框架,它利用零拷贝技术实现了高效的数据共享和传输。

  4. 数据库 在数据库应用中,零拷贝技术可以减少数据在内存和磁盘之间的复制,提高查询和事务处理的效率。例如,MongoDB和PostgreSQL都利用了零拷贝技术。

  5. 虚拟化领域 在虚拟化技术中,如KVM(Kernel-based Virtual Machine),可通过零拷贝技术减少虚拟机间网络数据的复制,提高虚拟机的网络性能。

  6. Apache Kafka是一个分布式流处理平台,用于处理和存储实时数据。Kafka使用零拷贝技术来提高数据传输的性能和效率,通过直接将数据从磁盘读取到网络缓冲区,避免了数据在内存中的多次复制。

  7. Netty被广泛用于构建高性能、高吞吐量的网络应用。Netty利用零拷贝技术来减少数据在内核和用户空间之间的复制,从而提高数据处理速度。

2.传统拷贝技术的缺点

传统的拷贝方式在数据传输过程中存在一些缺点,包括系统调用与数据拷贝、CPU开销和内存带宽的浪费以及对网络传输的影响。
在这里插入图片描述
传统方式的上下文切换
在这里插入图片描述

  1. 系统调用与数据拷贝
    在传统的拷贝方式中,数据从输入/输出设备的缓冲区传输到应用程序的内存空间需要经过多次的系统调用和数据拷贝。首先,数据从设备驱动程序的内核缓冲区拷贝到内核空间的缓冲区。然后,通过系统调用将数据从内核空间的缓冲区拷贝到用户空间的缓冲区。最后,应用程序才能处理这些数据。这种多次的系统调用和数据拷贝会增加系统的开销和延迟。

  2. CPU开销和内存带宽的浪费:
    传统的拷贝方式需要CPU参与数据的复制操作,即数据从内核空间的缓冲区拷贝到用户空间的缓冲区。这样会导致CPU的大量时间和资源被占用于数据拷贝操作,造成CPU开销的增加。另外,每次数据拷贝都需要使用内存带宽,占用了宝贵的内存资源,降低了内存带宽的利用效率。

  3. 对网络传输的影响:
    在网络传输中,传统的拷贝方式会引入额外的数据拷贝操作,增加了数据在网络中的传输开销。数据从应用程序的内存空间拷贝到网络缓冲区,再从网络缓冲区拷贝到网络传输的目的地,这些额外的数据拷贝会增加网络传输的延迟,并降低网络传输的性能。

综上所述,传统的拷贝方式存在系统调用与数据拷贝、CPU开销和内存带宽的浪费,以及对网络传输的影响等缺点。这些缺点限制了数据传输的效率和性能,因此需要引入零拷贝技术来优化数据传输过程。

3. 零拷贝技术的原理与实现

零拷贝技术是一种优化数据传输的技术,它的核心思想是减少或避免数据在内核空间和用户空间之间的多次拷贝,从而提高数据传输的效率和性能。

零拷贝的传输方式
在这里插入图片描述
使用 TransferTo() 进行上下文切换
在这里插入图片描述

1. sendfile系统调用

sendfile系统调用是一种高效的数据传输方式,允许在文件描述符和套接字描述符之间发送数据,无须将数据复制到内核空间和用户空间。它在Linux和其他类Unix操作系统中可用,为网络应用程序提供了高性能的数据传输。

  1. 零拷贝(Zero-copy)sendfile系统调用避免了内核空间和用户空间之间的数据复制。传统的数据传输方式需要将数据从文件描述符复制到用户空间,然后再从用户空间复制到套接字描述符。这种方式会导致CPU和内存的额外开销。而sendfile系统调用直接在内核空间完成数据传输,从而消除了数据拷贝开销。

  2. 避免上下文切换,sendfile系统调用在内核空间完成数据传输,因此无需在用户空间和内核空间之间进行上下文切换。这可以降低CPU使用率,提高数据传输性能。

  3. 高效的缓存利用,sendfile系统调用可以利用操作系统的文件系统缓存。当文件数据已经存在于文件系统缓存中时,sendfile可以直接从缓存中读取数据,而无需进行磁盘I/O操作。这可以提高数据读取速度,缩短数据传输延时。

sendfile系统调用的原型:
sendfile系统调用返回实际传输的字节数,或在发生错误时返回-1。在成功调用sendfile之后,offset参数会被更新为文件中新的读取位置。

#include <sys/sendfile.h>

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
  • out_fd:目标套接字文件描述符,通常是一个已连接的TCP套接字;
  • in_fd:源文件描述符,指向要传输的文件;
  • offset:文件中的起始偏移量,指定从哪个位置开始传输数据。如果为NULL,则从文件的当前位置开始传输;
  • count:要传输的字节数。如果为0,则传输整个文件。

sendfile系统调用是一种高效的数据传输方式,充分利用操作系统内核和文件系统缓存,减少CPU和内存开销。在网络应用程序和文件传输等场景中,sendfile可以大大提高性能。

2. 内核缓冲区与用户缓冲区

零拷贝技术利用内核缓冲区和用户缓冲区之间的共享,避免了多次数据拷贝。在传统的拷贝方式中,数据需要从内核缓冲区拷贝到用户缓冲区,而在零拷贝技术中,数据可以直接从内核缓冲区传输到用户缓冲区,减少了一次数据拷贝操作。

3. DMA(Direct Memory Access)技术

DMA技术是零拷贝技术的重要支持。DMA是一种直接内存访问技术,它可以让外设(如网络卡、磁盘控制器等)直接访问系统内存,而不需要经过CPU的参与。通过使用DMA技术,数据可以直接从外设传输到内存,避免了CPU的拷贝操作,减少了CPU的开销和内存带宽的浪费。

利用 DMA 技术减少 2 次 CPU 全程参与的拷贝
在这里插入图片描述

4. 文件描述符传递与共享

零拷贝技术通过传递文件描述符来共享文件或数据的访问权限,避免了数据在内核空间和用户空间之间的拷贝。应用程序可以将文件描述符传递给其他进程,其他进程可以直接使用该文件描述符来访问相同的文件或数据,而不需要进行数据拷贝操作。
在这里插入图片描述

利用传递文件描述符代替内核中的数据拷贝

5. Direct I/O(直接I/O)

Direct I/O(直接I/O)是一种绕过操作系统缓存机制的文件I/O访问方式。在传统的文件I/O操作中,操作系统会将文件数据缓存在内存中,这称为缓冲I/O(buffered I/O)。当应用程序需要访问文件时,操作系统会先检查缓存,如果数据已经在缓存中,就无需从磁盘读取,从而提高了访问速度。然而,在某些场景下,如数据量巨大、一次性读取或者特定的性能要求下,缓冲I/O可能会带来内存消耗和数据拷贝的开销。

Direct I/O解决了这个问题。它允许应用程序直接从磁盘读取或写入数据,绕过操作系统的缓存,从而减少内存消耗和数据拷贝。这在某些特定场景下可以提高I/O性能和效率,如数据库、文件服务器和大数据处理等。

直接I/O有以下特点:

  1. 减少内存占用:数据不再缓存在内存中,有助于降低内存压力,尤其在大文件处理过程中。
  2. 避免缓存污染:在某些情况下,频繁读取的大文件可能会“污染”操作系统的缓存,导致其他频繁使用的数据被替换出缓存。直接I/O避免了这种情况的发生。
  3. 减少数据拷贝:直接I/O可以减少数据在内核空间和用户空间之间的拷贝次数,提高性能。

但是,直接I/O并不总是比缓冲I/O性能更好。由于绕过了操作系统的缓存,直接I/O可能在某些情况下导致更频繁的磁盘访问,降低性能。

参考资料

  1. https://developer.ibm.com/articles/j-zerocopy/
  2. https://en.wikipedia.org/wiki/Zero-copy

猜你喜欢

转载自blog.csdn.net/wangshuai6707/article/details/133307033