一、用户态与内核态

 在现代操作系统中,为了保障系统的安全与稳定,CPU 通常支持两种运行模式:用户态 和 内核态。

  • 用户态:用户程序运行的状态,权限较低,不能直接访问硬件或内核内存空间。当进程执行用户自己的代码时,就处于用户态。
  • 内核态:操作系统内核运行的状态,拥有最高权限,可以直接访问所有硬件资源(如磁盘、网卡、内存等)和内核空间的数据。当进程通过系统调用进入内核代码时,就切换到内核态。

 区分用户态和内核态的主要目的是隔离保护:防止用户程序的错误或恶意行为破坏系统核心。应用程序需要操作硬件或执行敏感操作时,必须通过系统调用(System Call)请求内核代为完成。

在这里插入图片描述

二、传统模式下的文件传输流程

 传统 I/O 方式中,将一个文件从磁盘发送到网络(例如 Web 服务器返回一个静态文件),通常使用 read() + write() 两个系统调用。整个过程涉及 4 次上下文切换 和 4 次数据拷贝(包括 2 次 CPU 拷贝和 2 次 DMA 拷贝)

 具体的步骤如下,

  1. 进程调用 read(),从用户态切换到内核态(第 1 次切换)。
  2. 内核通过 DMA(Direct Memory Access,直接内存访问)控制器将文件数据从磁盘拷贝到内核缓冲区(第 1 次拷贝,DMA 拷贝)。
  3. CPU 将数据从内核缓冲区拷贝到用户缓冲区(第 2 次拷贝,CPU 拷贝)。
  4. read() 返回,从内核态切换回用户态(第 2 次切换)。
  5. 进程调用 write(),再次从用户态切换到内核态(第 3 次切换)。
  6. CPU 将数据从用户缓冲区拷贝到Socket 缓冲区(第 3 次拷贝,CPU 拷贝)。
  7. DMA 将数据从 Socket 缓冲区拷贝到网卡,进行网络传输(第 4 次拷贝,DMA 拷贝)。
  8. write() 返回,从内核态切换回用户态(第 4 次切换)。

在这里插入图片描述

三、零拷贝技术下的解决方案

 零拷贝(Zero-Copy) 技术通过消除或减少 CPU 参与的数据拷贝次数,大幅提升 I/O 吞吐量、降低延迟。其核心思想是:让数据尽量在内核态内部直接流转,避免在用户态和内核态之间来回复制。

 现代操作系统提供多种零拷贝机制,其中最经典且效果显著的是 sendfile() 系统调用。它允许在两个文件描述符之间直接传输数据,完全在内核态完成,无需将数据拷贝到用户空间。

1.传统 sendfile

 传统的 sendfile() 实现(未支持 SG-DMA 或 DMA Gather Copy 的老式网卡/驱动)虽然减少了上下文切换次数,但仍然需要一次 CPU 拷贝,结果共 2 次上下文切换,3 次数据拷贝(其中 1 次为 CPU 拷贝)。相比传统 read+write(4 次切换,4 次拷贝)已有明显提升,但 CPU 仍需参与一次数据搬运。。其流程如下:

  • 进程调用 sendfile(),从用户态切换到内核态(第 1 次切换)。
  • DMA 将文件数据从磁盘拷贝到内核缓冲区(第 1 次拷贝,DMA 拷贝)。
  • CPU 将数据从内核缓冲区拷贝到 Socket 缓冲区(第 2 次拷贝,CPU 拷贝)。
  • DMA 将数据从 Socket 缓冲区拷贝到网卡(第 3 次拷贝,DMA 拷贝)。
  • sendfile() 返回,从内核态切换回用户态(第 2 次切换)。

在这里插入图片描述

2.sendfile + SG-DMA

 借助支持 DMA Gather Copy 或 SG-DMA 的网卡,sendfile() 可以实现仅 2 次上下文切换 和 2 次 DMA 拷贝(完全消除 CPU 拷贝)

  1. 进程调用 sendfile(),从用户态切换到内核态(第 1 次切换)。
  2. DMA 将文件数据从磁盘拷贝到内核缓冲区(第 1 次 DMA 拷贝)。
  3. 内核仅将数据的位置和长度信息(文件描述符、偏移量等)传递给网卡,网卡通过 DMA Gather 直接从内核缓冲区读取数据并发送,无需 CPU 拷贝。
  4. sendfile() 返回,从内核态切换回用户态(第 2 次切换)。

在这里插入图片描述

四、零拷贝技术的实际应用


应用/框架 使用的零拷贝技术 说明
Kafka sendfile / transferTo 消息日志文件从磁盘发送到网络时,利用零拷贝大幅提高吞吐量(Kafka 官方宣传零拷贝是其高性能关键之一)。
RocketMQ mmap + write / sendfile 在消息存储和同步刷盘时,结合零拷贝优化文件读写性能。
Netty FileRegion (基于 sendfile 或 transferTo) 用于高效传输文件内容,常用于 HTTP 静态文件服务、代理等。
Nginx sendfile 指令 开启 sendfile on; 后,Nginx 在处理静态文件请求时自动使用零拷贝,极大降低 CPU 占用。
Apache Tomcat sendfile(通过 Sendfile 端点) 处理大型静态资源时启用零拷贝加速。
Redis writev(分散-聚集写)与零拷贝思路结合 在输出缓冲区处理时减少数据复制。
数据库系统(如 Cassandra、RocksDB) sendfile, splice 用于快速备份、数据迁移、日志传输。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐