sendfile 指令通过内核态直传绕过用户空间,将数据搬运从4次减至2次、上下文切换从4次减至2次,显著降低CPU开销与延迟;仅对本地文件生效,需配合tcp_nopush on且关闭gzip等运行时干预才能确保零拷贝路径全程畅通。

sendfile 指令对文件读取性能的直接优化,核心在于绕过用户态、减少数据搬运次数,从而显著降低 CPU 开销和延迟。它不是加快磁盘读速,而是让“读出来之后怎么送出去”这个环节更高效。
跳过用户空间拷贝,节省两次 CPU 搬运
传统 read/write 方式下,一个静态文件从磁盘到网络需经历:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → 用户态缓冲区(CPU 拷贝)
- 用户态缓冲区 → socket 缓冲区(CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
启用 sendfile 后,路径缩短为:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → socket 缓冲区(内核态直传,无 CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
省掉两次 CPU 拷贝和两次上下文切换,CPU 使用率可下降 30%–50%,尤其在高并发小文件或大文件场景中效果明显。
依赖真实文件系统,仅对本地磁盘文件生效
sendfile 的零拷贝能力需要底层文件系统支持(如 ext4、xfs),以下情况会自动失效:
- 文件位于 NFS、CIFS 或某些容器卷(如 overlay2 默认不透传 sendfile)
- 使用 alias 指向非 root 下的路径,且 Nginx 版本较旧(部分 1.18 之前版本有兼容问题)
- 响应来自 proxy_pass、fastcgi_pass 等后端,即使返回 Content-Length 也不走 sendfile 路径
验证是否生效:用 strace -p $(pgrep nginx) -e trace=sendfile64 观察 worker 进程是否持续调用该系统调用;若无输出或频繁出现 “sendfile() failed”,说明未真正启用。
必须配合 tcp_nopush 才能释放全部效能
sendfile 只解决“怎么搬”,tcp_nopush 解决“怎么发”。二者绑定生效:
- tcp_nopush on 会让内核把响应头 + 文件起始数据合并进同一个 TCP 包,提升单包载荷率
- 若只开 sendfile 不开 tcp_nopush,数据仍可能被拆成多个小包发送,网络效率打折扣
- 同时需设 tcp_nodelay off,否则 Nagle 算法会被强制绕过,抵消 tcp_nopush 的攒包效果
这不是可选搭配,而是硬性协同关系——Nginx 日志里若出现 “tcp_nopush is ignored”,基本说明 sendfile 当前未实际工作。
避免运行时干预,守住零拷贝路径
哪怕配置了 sendfile on,只要请求触发以下任一行为,Nginx 就会悄悄退回到 read/write 模式:
- 启用了 gzip on(压缩必须进用户态)
- 客户端发起 Range 请求(如视频拖拽)
- 配置了 etag、expires 或 add_header 动态生成响应头
- 使用 sub_filter 修改响应体内容
推荐做法:对纯静态资源 location 显式关闭干扰项,例如:
gzip off;
etag off;
sub_filter_once on;(若必须用)



















