sendfile 通过内核态零拷贝规避用户态参与导致的阻塞放大,减少两次内存拷贝、三次上下文切换及 cpu 开销,提升小文件传输效率。

sendfile 本身不直接“解决”磁盘 I/O 阻塞,而是通过内核态零拷贝机制,规避因用户态参与导致的阻塞放大。真正影响阻塞的,是它是否与底层 I/O 行为协同得当。
绕过用户态拷贝,减少上下文切换和内存压力
传统 read() + write() 流程需两次系统调用、两次内存拷贝、三次上下文切换;而 sendfile() 让数据在内核中直接从文件 fd 传到 socket fd,全程不经过用户空间。这显著降低 CPU 开销和延迟,尤其对小文件(
- 适用于 CSS、JS、图片等静态资源服务场景
- 必须配合 tcp_nopush on; 才能填满 TCP 报文段,避免小包堆积
- 若启用 gzip、sub_filter 等内容改写模块,sendfile 会自动禁用——此时需权衡压缩收益与 I/O 开销
避免 page cache 争抢引发的隐性阻塞
当大文件未被缓存、又未配 AIO 或 directio 时,sendfile 可能触发同步磁盘读(如 ext4/xfs 默认行为),导致 worker 进程卡住。这不是 sendfile 的缺陷,而是它依赖 page cache 的副作用。
- 小文件:靠 page cache + sendfile 即可,无需额外配置
- 大文件(≥ 4MB):需关闭 sendfile,改用 directio 4m; + aio on; 绕过页缓存、异步读盘
- 注意:aio 和 sendfile 互斥;启用前者会自动关闭后者
防止单请求长期占用 worker 进程
一个超大文件响应若全程由 sendfile 同步完成,可能持续占用 worker 数百毫秒甚至更久,拖慢其他连接处理。这时 sendfile_max_chunk 就起关键作用。
- 设置 sendfile_max_chunk 512k; 可限制单次传输量,让事件循环有机会调度其他请求
- 该参数仅在未启用 aio 或 threads 模式时最有效
- Linux 5.3+ 可结合 io_uring 实现更高效的异步 sendfile,但需内核与编译支持
不适用的典型场景
sendfile 是读优化手段,对以下情况无改善甚至无效:
- 反向代理场景(后端是动态应用),数据流不经过本地文件系统
- 需要修改响应体(如添加 header、替换内容、gzip 压缩)
- 写操作(如上传、日志落盘)—— nginx 不支持异步写 sendfile
- 高并发随机小文件读,若 open_file_cache 未合理配置,元数据频繁重载也会成为瓶颈











