开启 sendfile 可显著降低 CPU 使用率,因其避免了传统 read/write 路径中的 2 次 CPU 拷贝和 4 次上下文切换,改为 0 次 CPU 拷贝和 2 次上下文切换,并需配合禁用 gzip、关闭动态响应头、使用本地文件及启用 tcp_nopush 才能 fully 生效。

开启 sendfile 能显著降低 CPU 使用率,核心在于它把原本必须由 CPU 参与的多次内存拷贝,直接交给内核在底层完成,大幅减少用户态与内核态之间的上下文切换和 CPU 拷贝操作。
传统 read/write 路径消耗大量 CPU
不启用 sendfile 时,Nginx 发送一个静态文件需走标准的“读-写”流程:
- 磁盘数据通过 DMA 进入内核页缓存
- CPU 将该数据从内核缓冲区拷贝到 Nginx 用户态内存(
read()系统调用) - CPU 再将用户态内存中的数据拷贝到 socket 发送缓冲区(
write()系统调用) - 最后由网卡 DMA 发出
这个过程共发生2 次 CPU 拷贝 + 4 次上下文切换,CPU 长时间忙于搬运数据,尤其在高并发或大文件场景下,CPU 使用率会快速飙升。
sendfile 路径让 CPU “只调度、不搬运”
启用后,Nginx 直接调用内核 sendfile64() 系统调用,数据流向变为:
- 磁盘 → 内核页缓存(DMA)
- 内核页缓存 → socket 缓冲区(内核态直传,无需 CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
整个过程0 次 CPU 拷贝 + 2 次上下文切换。CPU 不再参与数据搬运,只负责发起系统调用和调度,负载自然大幅下降。
真正生效的前提是避开退化路径
即使配置了 sendfile on,以下情况会让 Nginx 自动回退到耗 CPU 的 read/write 模式:
- 启用了
gzip on(压缩必须进用户态) - 响应头被动态修改(如
etag on、expires、add_header) - 文件来自 NFS/CIFS 或加密文件系统(不支持内核零拷贝接口)
- 客户端发起了 Range 请求(需切片,无法整块搬运)
所以单纯开开关不够,必须配套禁用 gzip、关闭动态头、确保文件本地存储且格式合规。
配合 tcp_nopush 才能发挥全部优势
tcp_nopush on 不是可选项,而是 sendfile 的搭档:
- 它让内核把响应头和 sendfile 数据“攒成一个 TCP 报文”再发出,避免小包泛滥
- 但该机制仅在 sendfile 生效时起作用;否则 Nginx 日志会提示 “tcp_nopush is ignored”
- 二者协同可进一步减少系统调用次数和网络协议栈处理开销
单独开其中任一指令,都无法达成 CPU 优化目标。











