sendfile 是静态资源服务的性能分水岭,必须同时满足真实磁盘文件、禁用gzip、无Range请求等条件才生效;否则自动退化为read/write模式。

sendfile 不是“开了就快”的开关,而是静态资源服务的性能分水岭——它让 Nginx 跳过用户态搬运,直接在内核完成磁盘到网卡的数据直传。但它的价值高度依赖场景:对真实磁盘上的静态文件(如 JS、图片、视频),开启后可降低 CPU 30%+、提升吞吐;对代理响应、动态内容或压缩中文件,则完全无效甚至被自动禁用。
为什么 sendfile 能显著提速?
核心在于“零拷贝”路径的建立:
- 传统 read/write 模式需 4 次数据拷贝 + 4 次上下文切换:磁盘→内核缓冲区→用户空间→socket 缓冲区→网卡
- 启用 sendfile 后仅需 2 次拷贝 + 2 次切换:磁盘→内核缓冲区→socket 缓冲区→网卡,全程不触达用户空间
- Linux 2.4+ 还支持 SG-DMA,可进一步省略内核缓冲区到 socket 的拷贝,逼近真正零拷贝
哪些场景下 sendfile 真正生效?
必须同时满足以下条件,Nginx 才会实际调用 sendfile() 系统调用:
- 请求的是真实磁盘文件(root 或 alias 指向本地文件系统路径)
- 未启用运行时 gzip(gzip on)、而是用 gzip_static 或干脆关闭压缩
- 未触发 Range 请求(如视频拖拽)、ETag/Expires 动态头计算
- 文件不在 NFS/CIFS 等不支持 sendfile 的远程文件系统上
- 未使用 X-Accel-Redirect 等内部重定向,或重定向后仍返回原始文件且未修改响应体
如何配齐才能稳定发挥 sendfile 效能?
单开 sendfile on 不够,需组合堵住退化漏洞:
- sendfile on; —— 主开关,启用内核直传
- tcp_nopush on; —— 配合 sendfile,攒满 TCP 包再发,避免小包泛滥
- gzip off; 或 gzip_static on; —— 避免运行时压缩破坏零拷贝路径
- open_file_cache max=1000 inactive=60s; —— 减少高并发下 open/stat 系统调用,防止用户态介入
怎么确认 sendfile 真正在工作?
不能只看配置,得验证实际行为:
- 用 strace -p $(pgrep nginx) -e trace=sendfile64 观察 worker 进程是否持续调用该系统调用
- 检查 error.log 是否出现 sendfile() failed 类报错(常见于 NFS 或权限问题)
- 对比压测结果:对 1MB 静态文件做 wrk 测试,开启前后看 QPS 提升与 CPU 占用下降幅度



















