Nginx 的 sendfile 指令是触发内核零拷贝的关键入口,需配合 tcp_nopush on 和 tcp_nodelay off 才生效,仅对未压缩、未代理、本地真实静态文件起作用,且需验证 sendfile64 系统调用是否被调用。

Nginx 的 sendfile 指令不是简单“打开一个开关”,而是触发内核级零拷贝路径的关键入口。它真正起效的前提是绕过用户态内存搬运,让数据在内核中从磁盘页缓存直接送入 socket 缓冲区——这大幅减少了 CPU 参与和上下文切换,从而提升文件传输性能。
核心原理:两次拷贝替代四次拷贝
传统 read() + write() 方式需经历:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → 用户空间(CPU 拷贝)
- 用户空间 → socket 缓冲区(CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
共 4 次拷贝 + 4 次上下文切换,CPU 被大量消耗在数据搬运上。
启用 sendfile on 后,Nginx 调用内核 sendfile64() 系统调用,路径变为:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → socket 缓冲区(内核态直传,无 CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
仅需 2 次拷贝 + 2 次上下文切换。Linux 2.4+ 还支持 SG-DMA,可进一步跳过内核缓冲区到 socket 的拷贝,实现更彻底的零拷贝。
必须配套的三项指令缺一不可
-
sendfile on;—— 启用零拷贝基础能力 -
tcp_nopush on;—— 让内核等待响应头与文件数据凑满一个 TCP 报文段再发,减少小包数量 -
tcp_nodelay off;—— 关闭 Nagle 算法;若设为on,会立即发包,直接抵消tcp_nopush效果
仅对真实静态文件生效,常见退化场景要避开
- 启用
gzip on、gunzip on或gzip_static on:压缩/解压必须进用户态,强制回退 - 使用
proxy_pass、fastcgi_pass、sub_filter等:触发缓冲或内容改写逻辑 - 文件位于 NFSv3、FUSE、部分容器卷或符号链接指向非本地路径:底层不支持
sendfile()系统调用 - 响应缺少
Content-Length:Nginx 无法预知大小,转为 chunked 编码,无法走零拷贝
提升稳定性和吞吐的实用配置建议
- 在静态资源 location 中加
access_log off;,避免高并发下日志 I/O 成瓶颈 - 全局
http块中配置open_file_cache max=10000 inactive=60s;,缓存文件句柄和元信息 - 加
sendfile_max_chunk 512k;,防止大文件单次传输阻塞事件循环,兼顾图片、JS、短视频首段加载 - 按后缀精准配置,例如:
location ~* \.(jpg|jpeg|png|webp|gif|avif)$ { root /var/www/static; sendfile on; tcp_nopush on; tcp_nodelay off; gzip off; expires 1h; }
验证是否真正生效,不能只看配置是否加载
- 用
strace -p $(pgrep nginx | head -1) -e trace=sendfile64,read,write观察 worker 进程是否调用sendfile64,而非read+write - 对比开启前后高并发压测下的 CPU
sy(系统态)占比:典型优化可降 30%–50% - 检查
/proc/<pid>/fd/</pid>下 socket fd 和文件 fd 是否出现在同一sendfile64调用链中
不复杂但容易忽略











