Skip to content

P76 什么是零拷贝?零拷贝如何减少 CPU 拷贝与上下文切换? ​

面试题:什么是零拷贝?传统 IO 和零拷贝(sendfile/mmap)的区别?

1. 传统 IO 的数据流程(一次文件发送给网络) ​

text
read():磁盘 → 内核缓冲区 → 用户缓冲区     (2 次拷贝 + 2 次上下文切换)
write():用户缓冲区 → 内核 Socket 缓冲区 → 网卡 (2 次拷贝 + 2 次上下文切换)

合计:4 次用户态/内核态上下文切换 + 4 次数据拷贝(其中 2 次是 CPU 拷贝)。

2. 零拷贝的核心 ​

减少/消除 CPU 参与的数据拷贝和上下文切换,让数据尽量在内核里流动。

① mmap(内存映射) ​

text
磁盘 → 内核缓冲区(mmap 映射到用户空间,无用户态拷贝)→ Socket 缓冲区 → 网卡

省掉一次"内核 → 用户"的 CPU 拷贝(读路径),上下文切换减少。

② sendfile(Linux 2.1+) ​

text
磁盘 → 内核缓冲区 → Socket 缓冲区 → 网卡
  • 全程不经过用户态:1 次系统调用,2 次上下文切换;
  • 数据拷贝从 4 次降到 2 次(仍是 CPU 拷贝,但内核里);

③ sendfile + DMA 收集(Linux 2.4+,真正接近"零") ​

text
磁盘 → 内核缓冲区(DMA 拷贝)
内核缓冲区 → Socket(只传描述信息,无数据拷贝)
Socket → 网卡(DMA 拷贝)

CPU 不搬数据,由 DMA(直接内存访问) 完成,只剩 2 次 DMA 拷贝 + 少量元数据传递——这就是"零拷贝"。

3. Java 里的零拷贝 ​

java
// NIO:sendfile
FileChannel in = FileChannel.open(path);
in.transferTo(0, in.size(), socketChannel);
  • Netty 的 FileRegion 底层就是 transferTo;
  • RocketMQ/Kafka 消息存储用 mmap 读写文件(MappedByteBuffer)。

4. 对比总结 ​

方案CPU 拷贝上下文切换
传统 read+write2 次(+2 次 DMA)4 次
mmap+write1 次4 次
sendfile1 次(+2 次 DMA)2 次
sendfile+DMA gather0 次(纯 DMA)2 次

5. 加分点 ​

  • 说清"零拷贝不是 0 拷贝,而是减少 CPU 参与的拷贝";
  • 提到 PageCache 的作用:磁盘读缓存,热点数据零磁盘 IO;
  • 追问"什么时候不能用零拷贝":数据需要跨进程/修改、需要落用户空间处理时不行。

一句话总结 ​

零拷贝通过 mmap/sendfile 让数据在内核态流转、由 DMA 搬运,把传统 4 次上下文切换 + 多次 CPU 拷贝降到 2 次切换 + 0 次 CPU 拷贝(sendfile+DMA gather),Netty/Kafka 都用它提升 IO 吞吐。

基于 VitePress 重建