Illustrated | Thoroughly understand zero-copy (Zero-Copy) technology

I. Introduction

Everything in the Linux system is a file. If you think about it carefully, many activities of the Linux system are nothing more than read operations and write operations . Zero copy appears to improve read and write performance.

Second, the basic process of data copying

The cache and memory capacity of the Linux system are limited, and more data are stored in the disk. For Web servers, it is often necessary to read data from disk to memory, and then transmit it to the user through the network card:

The above data flow is just a big box, let's take a look at several modes.

2.1 CPU only mode

When an application needs to read disk data, it calls read() from user mode to fall into kernel mode, and the read() system call is finally completed by the CPU;

The CPU initiates an I/O request to the disk, and the disk starts to prepare data after receiving it;

After the disk puts the data in the disk buffer, it initiates an I/O interrupt to the CPU, reporting that the CPU data is Ready;

After the CPU receives the I/O interrupt from the disk controller, it starts to copy the data. After completion, read() returns, and then switches from the kernel state to the user state;

\

2.2 CPU&DMA mode

CPU time is precious, and letting it do chores is a waste of resources.

Direct Memory Access (Direct Memory Access) is a mechanism for hardware devices to bypass the CPU and directly access memory independently. Therefore, DMA liberates the CPU to a certain extent, and allows the hardware to do the chores of the previous CPU directly, which improves the CPU efficiency.

Currently, the hardware that supports DMA includes: network card, sound card, graphics card, disk controller, etc.

With the participation of DMA, the process has changed a few times:

\

The main change is that the CPU no longer interacts directly with the disk, but the DMA interacts with the disk and copies data from the disk buffer to the kernel buffer, and the process is similar thereafter.

[ Knock on the blackboard ] No matter from the CPU-only mode and the DMA&CPU mode, there are multiple redundant data copies and switching between the kernel mode and the user mode.

我们继续思考 Web 服务器读取本地磁盘文件数据再通过网络传输给用户的详细过程。

三,普通模式数据交互

一次完成的数据交互包括几个部分:系统调用 syscall、CPU、DMA、网卡、磁盘等。

\

系统调用 syscall 是应用程序和内核交互的桥梁,每次进行调用/返回就会产生两次切换:

调用 syscall 从用户态切换到内核态

syscall 返回 从内核态切换到用户态

来看下完整的数据拷贝过程简图:

读数据过程:

应用程序要读取磁盘数据,调用 read()函数从而实现用户态切换内核态,这是第 1 次状态切换;

DMA 控制器将数据从磁盘拷贝到内核缓冲区,这是第 1 次 DMA 拷贝;

CPU 将数据从内核缓冲区复制到用户缓冲区,这是第 1 次 CPU 拷贝;

CPU 完成拷贝之后,read()函数返回实现用户态切换用户态,这是第 2 次状态切换;

写数据过程:

应用程序要向网卡写数据,调用 write()函数实现用户态切换内核态,这是第 1 次切换;

CPU 将用户缓冲区数据拷贝到内核缓冲区,这是第 1 次 CPU 拷贝;

DMA 控制器将数据从内核缓冲区复制到 socket 缓冲区,这是第 1 次 DMA 拷贝;

完成拷贝之后,write()函数返回实现内核态切换用户态,这是第 2 次切换;

综上所述:

读过程涉及 2 次空间切换、1 次 DMA 拷贝、1 次 CPU 拷贝;

写过程涉及 2 次空间切换、1 次 DMA 拷贝、1 次 CPU 拷贝;

可见传统模式下,涉及多次空间切换和数据冗余拷贝,效率并不高,接下来就该零拷贝技术出场了。

四,零拷贝技术

4.1 出现原因

我们可以看到,如果应用程序不对数据做修改,从内核缓冲区到用户缓冲区,再从用户缓冲区到内核缓冲区。两次数据拷贝都需要 CPU 的参与,并且涉及用户态与内核态的多次切换,加重了 CPU 负担。

我们需要降低冗余数据拷贝、解放 CPU,这也就是零拷贝 Zero-Copy 技术。

4.2 解决思路

目前来看,零拷贝技术的几个实现手段包括:mmap+write、sendfile、sendfile+DMA 收集、splice 等。

4.2.1 mmap 方式

mmap 是 Linux 提供的一种内存映射文件的机制,它实现了将内核中读缓冲区地址与用户空间缓冲区地址进行映射,从而实现内核缓冲区与用户缓冲区的共享。

这样就减少了一次用户态和内核态的 CPU 拷贝,但是在内核空间内仍然有一次 CPU 拷贝。

mmap 对大文件传输有一定优势,但是小文件可能出现碎片,并且在多个进程同时操作文件时可能产生引发 coredump 的 signal。

4.2.2 sendfile 方式

mmap+write 方式有一定改进,但是由系统调用引起的状态切换并没有减少。

sendfile 系统调用是在 Linux 内核 2.1 版本中被引入,它建立了两个文件之间的传输通道。

sendfile 方式只使用一个函数就可以完成之前的 read+write 和 mmap+write 的功能,这样就少了 2 次状态切换,由于数据不经过用户缓冲区,因此该数据无法被修改。

从图中可以看到,应用程序只需要调用 sendfile 函数即可完成,只有 2 次状态切换、1 次 CPU 拷贝、2 次 DMA 拷贝。

但是 sendfile 在内核缓冲区和 socket 缓冲区仍然存在一次 CPU 拷贝,或许这个还可以优化。

4.2.3 sendfile+DMA 收集

Linux 2.4 内核对 sendfile 系统调用进行优化,但是需要硬件 DMA 控制器的配合。

升级后的 sendfile 将内核空间缓冲区中对应的数据描述信息(文件描述符、地址偏移量等信息)记录到 socket 缓冲区中。

DMA 控制器根据 socket 缓冲区中的地址和偏移量将数据从内核缓冲区拷贝到网卡中,从而省去了内核空间中仅剩 1 次 CPU 拷贝。

这种方式有 2 次状态切换、0 次 CPU 拷贝、2 次 DMA 拷贝,但是仍然无法对数据进行修改,并且需要硬件层面 DMA 的支持,并且 sendfile 只能将文件数据拷贝到 socket 描述符上,有一定的局限性。

4.2.4 splice 方式

splice 系统调用是 Linux 在 2.6 版本引入的,其不需要硬件支持,并且不再限定于 socket 上,实现两个普通文件之间的数据零拷贝。

splice 系统调用可以在内核缓冲区和 socket 缓冲区之间建立管道来传输数据,避免了两者之间的 CPU 拷贝操作。

splice 也有一些局限,它的两个文件描述符参数中有一个必须是管道设备。

五,本文小结

本文通过介绍数据交互的基本过程、传统模式的缺点,进而介绍了零拷贝的一些实现方法。

零拷贝技术是非常底层且重要的读写优化,对于服务并发能力的提升有很大帮助。

Guess you like

Origin juejin.im/post/7083793623594041375