sendfile零拷贝需sendfile与tcp_nopush协同启用,并禁用gzip、sub_filter等触发用户态处理的配置,同时确保文件真实、socket已连接、内核支持mmap及config_sock_sendpage。

核心是让 sendfile 真正走零拷贝路径,而不是“开了开关就自动生效”。它依赖配置协同、内核支持和规避干扰项,重点不在开启本身,而在确保整个链路不退化。
必须配对启用的关键指令
sendfile 单独开启无效,必须与 tcp_nopush 联动:
- sendfile on; —— 启用内核态直传,跳过用户空间缓冲区
- tcp_nopush on; —— 让 sendfile 输出的数据尽可能填满 TCP 报文段,减少小包数量和 write() 系统调用频次
- 若需低延迟(如 API 响应),可保留 tcp_nodelay on;;现代 Linux 内核支持 tcp_nopush 与 tcp_nodelay 共存,无需二选一
避开让零拷贝失效的配置
以下任一配置都会强制 Nginx 回退到 read/write 模式,数据必须进用户态处理,彻底失去零拷贝收益:
- 启用 gzip on; 或 gzip_static on;(压缩需读取并修改响应体)
- 使用 sub_filter(内容替换必须在用户态完成)
- 开启 etag on;、expires 或 add_header 修改响应头(触发响应体重写逻辑)
- 在 location 中混用 alias 与正则匹配(可能引发内部重写,破坏 sendfile 路径)
纯静态资源服务建议统一关闭:gzip off; etag off; expires off;
确保底层运行条件满足
sendfile 不是“配置即生效”,它依赖真实文件、socket 连接状态和内核能力:
- 源文件必须是 open() 打开的真实磁盘文件(如 /var/www/logo.png),不支持 /proc、tmpfs、管道或 socket
- 目标 fd 必须是 已建立连接的 TCP socket(UDP 也可,但单次传输不能超 MTU)
- 文件系统需支持 mmap 语义(ext4/xfs 默认支持;NFSv4 可行,NFSv3 或 noac 挂载易阻塞)
- Linux 内核 ≥ 2.4(全满足),且编译时启用 CONFIG_SOCK_SENDPAGE(主流发行版默认开启)
按文件大小做适配调度
小文件和大文件对零拷贝的利用方式不同,一刀切反而拖慢整体:
- 小文件(:频繁调用 sendfile 增加 syscall 开销。建议合并发送(如用 writev)或启用 TCP_CORK 暂存,等凑够 MSS 再发
- 中等文件(1KB–4MB):sendfile 效果最佳,保持默认配置即可
- 大文件(≥ 4MB):启用 directio 4m; 和 aio on;,绕过 page cache 直读磁盘,避免污染缓存;注意 directio 与 sendfile 互斥,此时应关闭 sendfile
不复杂但容易忽略











