Nginx 降低数据拷贝开销的核心是协同 Linux page cache、sendfile 零拷贝及 open_file_cache 元数据缓存。非阻塞 I/O 提供并发调度能力,三者分层协作实现低开销高复用:page cache 复用内存中文件数据,sendfile 实现内核态零拷贝直通,open_file_cache 减少 stat/open 系统调用开销。

Nginx 的非阻塞 I/O 本身不直接减少数据拷贝,真正降低拷贝开销的是它与 Linux 内核态缓存(page cache)的协同机制。关键不是“绕开内核”,而是让非阻塞模型在内核已缓存数据的前提下,用最短路径把数据送到客户端。
依赖 page cache 实现零磁盘读取复用
当 Nginx 处理静态小文件请求(如 JS、CSS、图标)时,它默认走标准缓存 I/O 路径:
- 内核先检查该文件是否已在 page cache 中
- 若命中,read() 系统调用直接返回缓存数据,不触发磁盘读取
- 即使 worker 进程是非阻塞的,只要数据已在内存中,read() 就几乎瞬间完成
这个过程对 Nginx 完全透明,无需额外配置——page cache 是 Linux 文件系统默认启用的机制,Nginx 默认就从中受益。
sendfile 实现内核态零拷贝直通
启用 sendfile on; 后,Nginx 不再调用 read() + write(),而是让内核直接把 page cache 中的数据复制到 socket 发送队列:
- 数据全程不经过用户态内存,省去两次系统调用和两次内存拷贝
- 整个流程仍保持非阻塞语义:若 socket 缓冲区满,内核返回
EAGAIN,Nginx 将连接挂回 epoll 可写事件队列,等通知再续传 - 要求响应体未被修改(即不能启用 gzip、sub_filter 等模块),且文件句柄为普通文件
避免 directio 破坏缓存有效性directio 指令会让读操作跳过 page cache,直接访问磁盘。这对小文件是负优化:
- 每次请求都触发真实磁盘 I/O,失去复用机会
- 应保持默认行为(不配置或显式设为
directio off;) - 仅在大文件分片场景(如视频流)中考虑
directio,并配合aio threads;防止阻塞事件循环
open_file_cache 减少元数据干扰
频繁的 stat()、open() 调用虽不读内容,但会穿透到内核查 inode 和权限,拖慢整体 I/O 路径:
-
open_file_cache max=10000 inactive=60s;缓存文件存在性、可读性、大小等元数据 -
open_file_cache_valid 60s;定期验证缓存项有效性 -
open_file_cache_min_uses 2;防止偶然访问污染缓存 -
open_file_cache_errors on;缓存“文件不存在”等错误结果,抵御扫描类攻击
本质上,非阻塞 I/O 提供了高并发调度能力,而 page cache + sendfile + open_file_cache 共同构成了一个低开销、高复用的数据通路。它们不是替代关系,而是分层协作:内核负责缓存和搬运,Nginx 负责事件驱动和策略调度。


















