定义:
在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。
好处:
零拷贝机制可以减少数据在内核缓冲区和用户进程缓冲区之间反复的 I/O 拷贝操作。 零拷贝机制可以减少用户进程地址空间和内核地址空间之间因为上下文切换而带来的 CPU 开销。
传统I/O方式:
零拷贝方式:
用户态直接 I/O:
应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输。这种方式依旧存在用户空间和内核空间的上下文切换,硬件上的数据直接拷贝至了用户空间,不经过内核空间。因此,直接 I/O 不存在内核空间缓冲区和用户空间缓冲区之间的数据拷贝。
mmap + write
mmap 是 Linux 提供的一种内存映射文件方法,即将一个进程的地址空间中的一段虚拟地址映射到磁盘文件地址
- 用户进程通过 mmap() 函数向内核发起系统调用,上下文从用户态切换为内核态。
2. 将用户进程的内核空间的读缓冲区与用户空间的缓存区进行内存地址映射。
-
CPU利用DMA控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。
-
上下文从内核态切换回用户态,mmap 系统调用执行返回。
-
用户进程通过 write() 函数向内核发起系统调用,上下文从用户态切换为内核态。
-
CPU将读缓冲区中的数据拷贝到的网络缓冲区。
-
CPU利用DMA控制器将数据从网络缓冲区拷贝到网卡进行数据传输。
-
上下文从内核态切换回用户态,write 系统调用执行返回。
sendfile
通过 sendfile 系统调用,数据可以直接在内核空间内部进行 I/O 传输,从而省去了数据在用户空间和内核空间之间的来回拷贝。与 mmap 内存映射方式不同的是, sendfile 调用中 I/O 数据对用户空间是完全不可见的。也就是说,这是一次完全意义上的数据传输过程。
- 用户进程通过 sendfile() 函数向内核发起系统调用,上下文从用户态切换为内核态。
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。
- CPU 将读缓冲区中的数据拷贝到的网络缓冲区。
- CPU 利用 DMA 控制器将数据从网络缓冲区拷贝到网卡进行数据传输。
- 上下文从内核态切换回用户态,sendfile 系统调用执行返回。
减少数据拷贝次数:
在数据传输过程中,避免数据在用户空间缓冲区和系统内核空间缓冲区之间的CPU拷贝,以及数据在系统内核空间内的CPU拷贝,这也是当前主流零拷贝技术的实现思路。
写时复制技术:写时复制指的是当多个进程共享同一块数据时,如果其中一个进程需要对这份数据进行修改,那么将其拷贝到自己的进程地址空间中,如果只是数据读取操作则不需要进行拷贝操作。
RocketMQ和Kafka对比
RocketMQ 选择了 mmap + write 这种零拷贝方式,适用于业务级消息这种小块文件的数据持久化和传输;
而 Kafka 采用的是 sendfile 这种零拷贝方式,适用于系统日志消息这种高吞吐量的大块文件的数据持久化和传输。但是值得注意的一点是,Kafka 的索引文件使用的是 mmap + write 方式,数据文件使用的是 sendfile 方式(通过 Java 的 FileChannel.transferTo 方法来实现的)。