【Linux】Linux 零拷贝技术全景解读:从内核到硬件的性能优化之道

学麻了,以下内容来自【腾讯元宝】。

Linux 零拷贝技术全景解读:从内核到硬件的性能优化之道

传统I/O的性能瓶颈:为什么需要零拷贝?

在深入探讨各种零拷贝技术之前,我们首先要理解传统I/O操作(即使用 readwrite 系统调用,并涉及用户空间指针 user_ptr)为何会成为性能瓶颈。

当需要读取一个文件并通过网络发送时,传统方式会触发以下步骤:

  1. read 系统调用 :用户进程发起读取请求,导致从用户态切换到内核态(第1次上下文切换)。
  2. DMA拷贝 :DMA控制器将数据从磁盘拷贝到内核缓冲区(第1次DMA拷贝,无CPU参与)。
  3. CPU拷贝 :CPU将数据从内核缓冲区拷贝到 user_ptr 所指向的用户缓冲区(第1次CPU拷贝)。随后,系统从内核态切换回用户态(第2次上下文切换)。
  4. write 系统调用 :用户进程发起写入请求,导致用户态再次切换到内核态(第3次上下文切换)。
  5. CPU拷贝 :CPU将数据从用户缓冲区拷贝到socket缓冲区(第2次CPU拷贝)。
  6. DMA拷贝 :DMA控制器将数据从socket缓冲区拷贝到网卡(第2次DMA拷贝)。最后,内核态切换回用户态(第4次上下文切换)。

这个过程包含了 4次上下文切换2次CPU拷贝2次DMA拷贝。其中,两次CPU拷贝(内核缓冲区 ↔ 用户缓冲区)消耗了宝贵的CPU周期,仅仅是为了搬运数据,而非处理业务逻辑。在高性能网络编程、大数据处理等I/O密集型场景中,这无疑是巨大的性能损耗。

零拷贝技术的演进与核心机制

零拷贝技术的核心目标就是减少甚至消除不必要的CPU数据拷贝,从而降低CPU占用、提升吞吐量、减少延迟。其实现并非单一技术,而是一系列在不同层面、针对不同场景的优化方案。

1. mmap:内存映射

mmap 通过内存映射 技术,将内核缓冲区直接映射到用户进程的地址空间。这样,应用程序就可以像操作普通内存一样直接访问文件数据,从而避免了 read 调用所引发的那一次从内核到用户的CPU拷贝。

  • 工作流程

    1. 调用 mmap 建立映射关系。
    2. 数据通过DMA从磁盘拷贝到内核缓冲区(第1次DMA拷贝)。
    3. 用户进程通过映射后的虚拟地址直接操作内核缓冲区中的数据,无需额外拷贝。
    4. 调用 write 时,数据仍需从内核缓冲区CPU拷贝到socket缓冲区(1次CPU拷贝)。
    5. 数据最终通过DMA从socket缓冲区拷贝到网卡(第2次DMA拷贝)。
  • 优势:减少了1次CPU拷贝,适合需要对文件内容进行修改的场景(如日志处理)。

  • 局限:仍有4次上下文切换和1次CPU拷贝;大文件映射可能导致内存占用过高。

2. sendfile:内核级数据搬运工

sendfile 系统调用允许数据在内核空间内直接从文件描述符传输到socket描述符 ,完全绕过用户空间。这消除了 mmap + write 方式中仍存在的一次CPU拷贝和两次上下文切换。

  • 工作流程(基础版)

    1. 调用 sendfile(out_fd, in_fd, ...)
    2. 数据通过DMA从磁盘拷贝到内核缓冲区。
    3. 数据从内核缓冲区CPU拷贝到socket缓冲区
    4. 数据通过DMA从socket缓冲区拷贝到网卡。
      这个过程只有2次上下文切换1次CPU拷贝
  • 工作流程(优化版:DMA Gather Copy)

    这是 sendfile 的终极形态。当底层硬件(如网卡)支持分散-聚集(Scatter-Gather) 功能时,可以实现真正的零CPU拷贝

    1. 调用 sendfile
    2. 数据通过DMA从磁盘拷贝到内核缓冲区。
    3. CPU不再拷贝数据本身,而是将数据在内存中的位置和信息(描述符)传递给网卡。
    4. 支持Gather功能的DMA控制器,根据这些描述符信息,直接从内核缓冲区读取数据并发送到网络 (第2次DMA拷贝)。
      这个过程实现了 0次CPU拷贝2次DMA拷贝,是效率最高的文件到网络传输方式。
  • 优势:极大减少了上下文切换和CPU拷贝,甚至实现零CPU拷贝。

  • 局限:数据对用户进程不可见,无法修改;通常只能用于文件到socket的传输。

3. splice:基于管道的通用零拷贝

splice 是另一个零拷贝系统调用,它不要求硬件特殊支持,而是利用管道作为中转站,在内核中移动数据。

  • 工作流程

    1. 调用 splice,将数据从源(如文件)引入一个管道。
    2. 再次调用 splice,将数据从管道导向目标(如socket)。
      整个过程数据始终在内核中流动,实现了0次CPU拷贝
  • 优势:纯软件实现,不依赖硬件;可用于任意两个文件描述符(但至少有一个必须是管道)。

  • 局限:两个文件描述符中必须有一个是管道。

4. DMA-BUF:设备间的零拷贝桥梁

前述技术主要优化的是数据在"内核-用户"之间或内核内部的流动。而 DMA-BUF 解决的则是一个更为核心的问题:如何在不同硬件设备(如GPU、摄像头、编码器)之间高效地共享大量数据,而无需CPU参与拷贝

  • 核心思想 :DMABUF 是 Linux 内核中用于在不同设备驱动或子系统之间共享内存缓冲区的框架。它通过一个文件描述符(fd) 来代表一块缓冲区。这个fd可以在进程间或驱动间传递,但数据本身始终停留在物理内存的同一位置。

  • 工作流程

    1. 导出:一个设备驱动(生产者,如GPU)分配一块缓冲区,并将其"导出"为一个DMABUF对象,获得一个fd。
    2. 传递:这个fd可以通过IPC(如Unix域套接字)传递给另一个设备驱动或进程(消费者,如显示驱动器)。
    3. 导入 :消费者通过fd"导入"这个DMABUF,获得直接访问该缓冲区的权限。
      这样,渲染好的图像数据就能从GPU直接送往显示器,全程无需CPU进行繁重的数据拷贝。
  • 关键优势

    • 真正的跨设备零拷贝:实现了设备到设备的直接DMA传输。
    • 内置同步机制 :提供 dma_fence 等机制,确保硬件操作的有序性(如等GPU渲染完,显示器再读取)。
    • 标准化框架:统一了Linux内核中图形、视频等子系统的缓冲区共享方式。
  • 典型应用:现代显示系统(Wayland)、摄像头到GPU再到编码器的多媒体流水线等。

下面的表格清晰地汇总了这几种核心技术的对比。

技术 核心原理 数据拷贝 (CPU / DMA) 上下文切换 关键优势 典型应用场景
传统 I/O 数据在用户缓冲区和内核缓冲区间显式来回拷贝。 2次 / 2次 4次 编程简单,数据可处理。 通用I/O。
mmap + write 将内核缓冲区映射到用户空间,进程直接访问。 1次 / 2次 4次 减少1次CPU拷贝,可处理数据。 处理大文件、需修改数据的场景。
sendfile 数据在内核空间直接从文件传输到socket。 1次 (基础) / 0次 (优化) / 2次 2次 避免用户空间参与,高效。 静态Web服务器(Nginx默认启用)。
sendfile + Gather DMA控制器根据描述符直接从内核缓冲区聚合数据发送。 0次 / 2次 2次 真正的零CPU拷贝,需要硬件支持。 高性能文件传输。
splice 利用管道在内核中连接两个文件描述符。 0次 / 2次 2次 不依赖硬件,可用于任意fd(至少一管道)。 流式处理、数据转发。
DMA-BUF 通过文件描述符(fd)在不同设备间共享同一块缓冲区。 0次 (设备间) / 视情况而定 视情况而定 实现跨设备零拷贝,硬件同步。 摄像头→GPU→显示器的多媒体流水线。

实践总结:如何选择零拷贝技术?

了解各种技术原理后,关键在于如何根据实际场景做出选择:

  • 需要对数据进行处理或修改 :选择 mmap。它允许你像操作内存一样处理文件数据,是数据库等应用的常见选择。
  • 纯文件转发,追求极致性能 :选择 sendfile。这是静态资源服务器(如Nginx)的首选,尤其是在支持DMA Gather Copy的系统上,性能最高。
  • 需要在内核空间进行灵活的数据搬运 :选择 splice。当数据需要在非普通文件描述符(如管道)之间流动时,它是理想工具。
  • 数据在多个硬件加速器之间流动 :这是 DMA-BUF 的专属领域。在涉及GPU、视频编解码器等多媒体图形处理时,必须使用此方案。(作者注:之前做Jetson零拷贝就是用这种方法。)

希望这篇整合的文章能够帮助你构建起对Linux零拷贝技术全面而清晰的认识。如果你对某个具体技术的实现细节或应用场景有更深入的疑问,我们可以继续探讨。

参考资料

相关推荐
ajassi20003 小时前
开源 Linux 服务器与中间件(六)服务器--Lighttpd
linux·服务器·开源
冲上云霄的Jayden3 小时前
MySQL InnoDB 状态(SHOW ENGINE INNODB STATUS)深度分析与性能优化建议
数据库·mysql·性能优化·innodb
立昂4 小时前
Linux route
linux·运维
milanyangbo4 小时前
从C10K到Reactor:事件驱动,如何重塑高并发服务器的网络架构
服务器·网络·后端·架构
爱吃生蚝的于勒4 小时前
【Linux】深入理解进程(一)
java·linux·运维·服务器·数据结构·c++·蓝桥杯
heisd_14 小时前
使用搭载Ubuntu的树莓派开启热点
linux·运维·ubuntu
阿干tkl4 小时前
优麒麟(Ubuntu Kylin) 安装向日葵远程工具/ToDesk
linux·ubuntu·kylin
Goafan4 小时前
ubuntu20.04插入USB耳机无法识别
linux
月球挖掘机4 小时前
华为USG防火墙之开局上网配置
服务器·网络