在传统的 Linux 程序中,如果需要读取外设中数据,发生的过程为:

  1. DMA 将数据拷贝到内核缓冲区

  2. CPU 将数据拷贝到用户空间。用户读取到数据

如果用户需要将数据写到设备中,发生的过程为

  1. CPU 将数据从用户空间拷贝到设备缓冲区

  2. DMA 从设备缓冲区读取数据并写到设备中

之所以出现数据从内核到用户空间的拷贝,根本原因是用户态进程没有对外设的直接访问能力。CPU 参与的拷贝过程会降低系统的吞吐量。零拷贝技术就是为了减少 CPU 拷贝。

mmap

首先出现的是 mmap,mmap 有两个步骤:

  • DMA 将数据拷贝到内核缓冲区

  • 内核将此缓冲区共享给用户态进程

从这个方面来看,mmap 相比 read 减少了一次 CPU 拷贝。但是对写数据没有帮助。

sendfile

如果只是用来发送文件,可以使用 sendfile 系统调用。其接口为

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset);

相比 read + write 的方式,其工作方式为:

  • DMA 将数据写到内核缓冲区

  • CPU 将数据拷贝到套接字缓冲区

  • DMA 将数据冲套接字缓冲区发送到网线

尽管 CPU 依然需要拷贝一次,但是由于只使用了一个系统调用,因此只陷入了一次内核

如果网卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技术,可以进一步提高性能。在此种情况下,sendfile 的工作方式为:

  • DMA 将数据写到内核缓冲区

  • 将缓冲区描述符和数据长度写到 socket 缓冲区中

  • 网卡的 SG-DMA 控制器直接将内核缓存中的数据拷贝到网卡的缓冲区并发送

此种情况下,陷入内核一次,CPU 参与拷贝零次

和 Linux 类似,Windows 也有类似的 transmitFile 函数
Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx