sendfile 能显著减少 cpu 开销,核心是绕过用户态、避免内存拷贝和上下文切换;需满足无运行时压缩、无动态响应头、无 range 请求、文件系统支持等条件才能生效,并需配合 gzip_static、tcp_nopush 等设置优化。

sendfile 能显著减少 CPU 处理数据的开销,核心在于绕过用户态、避免内存拷贝和上下文切换。
避开用户空间拷贝
传统文件传输需经历两次用户态参与:
- 内核把文件读入内核缓冲区(DMA 完成)
- CPU 把数据从内核缓冲区复制到用户空间缓冲区
- CPU 再把用户空间数据复制回内核网络缓冲区
- 最后由 DMA 发送到网卡
sendfile 直接让内核把磁盘缓冲区数据送入网络协议栈,跳过全部用户空间操作,只保留一次内核内拷贝(或零拷贝路径下完全由 DMA 链式搬运),大幅降低 CPU 搬运负担。
减少上下文切换次数
每次进出用户态都触发一次上下文切换,消耗数百纳秒到微秒级 CPU 时间。传统 read+write 流程需 4 次切换(read 进/出 ×2,write 进/出 ×2);而 sendfile 全程在内核态完成,仅需 1 次系统调用入口切换。高并发下,这项优化直接缓解 CPU 调度压力。
依赖条件必须满足才能生效
sendfile 不是“一开就灵”,以下任一情况都会导致它自动退化为 read+write:
- 启用了 gzip on(运行时压缩必须进用户态)
- 配置了 etag、expires 或其他需动态生成响应头的指令
- 请求带 Range 头(如视频拖拽),或 gzip_static 启用但 .gz 文件缺失
- 静态文件放在 NFS、CIFS、FUSE 或加密文件系统上(内核不支持零拷贝)
确认是否真实生效,可用 strace -e trace=sendfile -p $(pgrep nginx) 观察 worker 进程是否调用 sendfile64 系统调用。
配套设置堵住 CPU 泄漏点
单靠 sendfile 不够,还需收敛其他干扰:
- 用
gzip_static on; gzip off;替代运行时压缩,让预压缩 .gz 文件直传,保持零拷贝畅通 - 开启
tcp_nopush on,配合 sendfile 一次性推送更大 TCP 包,减少协议栈处理频次 - 禁用正则 location 匹配(如
location ~ \.js$),改用前缀匹配(location /static/),省去 PCRE 解析开销 - 启用
open_file_cache缓存文件句柄与元信息,避免高频 stat/open 系统调用











