sendfile 是 nginx 静态资源服务高效的核心机制,通过内核态直传实现零拷贝(2次dma拷贝),需与 tcp_nopush on 和 tcp_nodelay off 协同配置,并规避 gzip、range 请求等导致的退化场景。

sendfile 是 Nginx 静态资源服务真正高效的底层开关,它不靠把文件“映进内存”,而是让数据从磁盘到网卡全程留在内核态,跳过用户空间搬运——这才是吞吐高、CPU 低、延迟稳的根本原因。
零拷贝路径:2次拷贝替代4次搬运
传统 read()+write() 模式需经历:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → 用户空间(CPU 拷贝)
- 用户空间 → socket 缓冲区(CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
启用 sendfile 后,路径大幅缩短:
- 磁盘 → 内核页缓存(DMA)
- 页缓存 → socket 缓冲区(内核直传,无 CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
Linux 2.4+ 还支持 SG-DMA,可进一步省去页缓存到 socket 的拷贝,实现更彻底的零拷贝。
必须成套配置,单开 sendfile on 不起作用
sendfile 不是独立生效的指令,它依赖协同配置才能稳定在线:
- sendfile on; —— 激活内核直送能力
- tcp_nopush on; —— 让 sendfile 数据攒满 TCP 包再发,减少小包和中断
- tcp_nodelay off; —— 关闭 Nagle 算法,避免与 tcp_nopush 冲突
三者缺一不可。若只开 sendfile,而 tcp_nopush 关闭或 tcp_nodelay 开启,实际传输仍可能退化为低效模式。
常见退化场景:看似开了,实则失效
即使配置正确,以下情况会让 Nginx 自动回退到 read()+write():
- 启用 gzip on(运行时压缩必须进用户态)
- 使用 etag、expires epoch 或动态过期头
- 客户端发起 Range 请求(如视频拖拽)
- 文件位于 NFS/CIFS 或某些容器存储挂载点
- location 中配置了 proxy_pass、sub_filter、add_before_body 等内容改写模块
验证是否真实生效,可用:strace -p $(pgrep nginx) -e trace=sendfile 观察 worker 进程是否持续调用 sendfile 系统调用。
配套机制:守住零拷贝,还要减少干扰
光有 sendfile 路径还不够,还需防止其他操作把它“挤出”内核态:
- 用 gzip_static on; 替代 gzip on,提前生成 .gz 文件,仍走 sendfile
- 启用 open_file_cache 缓存句柄与元数据,避免高频 open()/stat() 触发用户态介入
- 静态 location 中关闭访问日志:access_log off;
- 用前缀匹配(如
location /static/ { })替代正则匹配,降低 CPU 开销
这些不是锦上添花,而是保障 sendfile 始终畅通的必要防线。











