sendfile 是 Nginx 降低系统调用次数最直接有效的机制之一,它将 read()+write() 的两次系统调用压缩为单次 sendfile() 调用,跳过用户态拷贝,减少上下文切换与 CPU 开销;需满足本地文件、禁用 gzip/sub_filter/proxy_buffering、含 Content-Length 等条件才能生效,并须与 tcp_nopush on 和 tcp_nodelay off 协同配置方可发挥最大效果。

sendfile 是 Nginx 降低系统调用次数最直接有效的机制之一。它把原本需要两次系统调用(read + write)的文件传输过程,压缩为单次 sendfile() 调用,同时跳过用户态内存拷贝,显著减少上下文切换和 CPU 开销。
为什么能减少系统调用
传统方式下,Nginx 读取静态文件需先 read() 把数据从内核页缓存复制到用户态缓冲区,再用 write() 把该缓冲区内容写入 socket。这涉及:
- 两次系统调用(read 和 write 各一次)
- 两次上下文切换(用户态 ↔ 内核态)
- 两次 CPU 参与的数据拷贝
启用 sendfile 后,内核直接在文件描述符(磁盘文件)和 socket 描述符之间完成数据搬运,整个过程不经过用户空间,只需一次系统调用,且数据搬运由 DMA 或内核内部零拷贝完成。
实际生效的前提条件
sendfile 不是“一开就灵”,必须满足以下条件才能真正触发:
- 服务的是本地磁盘文件(ext4/xfs 等),不能是 NFS、FUSE、加密文件系统等
- 响应未被运行时修改:禁用 gzip、sub_filter、proxy_buffering、ETag 动态生成等
- HTTP 连接保持活跃(keep-alive 或 HTTP/2),避免短连接导致 page cache 失效
- 响应头中包含 Content-Length(尤其在代理场景下需后端明确返回)
任一条件不满足,Nginx 会自动退回到 read/write 模式,系统调用次数立即翻倍。
协同配置才能发挥最大效果
单独开启 sendfile 效果有限,需搭配其他指令形成闭环优化:
- tcp_nopush on;:让内核把响应头和 sendfile 数据合并成一个 TCP 报文发出,避免小包堆积和额外 write 调用
- tcp_nodelay off;:与 tcp_nopush 配合使用,防止 Nagle 算法干扰“攒包”逻辑
- gzip_static on;:若需压缩,优先提供预压缩文件(如 .js.gz),仍可走 sendfile 路径
- sendfile_max_chunk 512k;:限制单次 sendfile 传输量,避免大文件阻塞事件循环
如何确认它真的在工作
不能只看配置,得验证运行时行为:
- 用
strace -e trace=sendfile,read,write -p $(pgrep nginx)抓取 worker 进程调用 - 正常应持续看到
sendfile64()调用,几乎不出现read()和write() - 若日志中出现 “tcp_nopush is ignored”,说明 sendfile 未生效,需回查退化原因
不复杂但容易忽略


















