零拷贝技术
一、用户态与内核态
在现代操作系统中,为了保障系统的安全与稳定,CPU 通常支持两种运行模式:用户态 和 内核态。
- 用户态:
用户程序运行的状态,权限较低,不能直接访问硬件或内核内存空间。当进程执行用户自己的代码时,就处于用户态。 - 内核态:
操作系统内核运行的状态,拥有最高权限,可以直接访问所有硬件资源(如磁盘、网卡、内存等)和内核空间的数据。当进程通过系统调用进入内核代码时,就切换到内核态。
区分用户态和内核态的主要目的是隔离保护:防止用户程序的错误或恶意行为破坏系统核心。应用程序需要操作硬件或执行敏感操作时,必须通过系统调用(System Call)请求内核代为完成。

二、传统模式下的文件传输流程
传统 I/O 方式中,将一个文件从磁盘发送到网络(例如 Web 服务器返回一个静态文件),通常使用 read() + write() 两个系统调用。整个过程涉及 4 次上下文切换 和 4 次数据拷贝(包括 2 次 CPU 拷贝和 2 次 DMA 拷贝)。
具体的步骤如下,
- 进程调用 read(),从用户态切换到内核态(第 1 次切换)。
- 内核通过 DMA(Direct Memory Access,直接内存访问)控制器将文件数据从磁盘拷贝到内核缓冲区(第 1 次拷贝,DMA 拷贝)。
- CPU 将数据从内核缓冲区拷贝到用户缓冲区(第 2 次拷贝,CPU 拷贝)。
- read() 返回,从内核态切换回用户态(第 2 次切换)。
- 进程调用 write(),再次从用户态切换到内核态(第 3 次切换)。
- CPU 将数据从用户缓冲区拷贝到Socket 缓冲区(第 3 次拷贝,CPU 拷贝)。
- DMA 将数据从 Socket 缓冲区拷贝到网卡,进行网络传输(第 4 次拷贝,DMA 拷贝)。
- 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 拷贝):
- 进程调用 sendfile(),从用户态切换到内核态(第 1 次切换)。
- DMA 将文件数据从磁盘拷贝到内核缓冲区(第 1 次 DMA 拷贝)。
- 内核仅将数据的位置和长度信息(文件描述符、偏移量等)传递给网卡,网卡通过 DMA Gather 直接从内核缓冲区读取数据并发送,无需 CPU 拷贝。
- 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 | 用于快速备份、数据迁移、日志传输。 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)