nginx默认不缓存文件描述符(fd),仅当open_file_cache_flags启用min_uses且满足多次命中、未失效等条件时才可能复用fd;cache回收的是ngx_open_file_cache_node_t元信息节点,通过LRU链表与定时expire机制管理。

nginx 的 ngx_open_file_cache_t 并不直接管理“文件句柄”的生命周期回收,它缓存的是 文件元信息(如 inode、mtime、size、权限、是否为目录等),而文件描述符(fd)本身在默认配置下并不被长期缓存。
文件描述符是否被缓存?取决于 open_file_cache_flags
只有当配置中显式启用了 open_file_cache_flags 且包含 open_file_cache_retest 或更关键的 open_file_cache_min_uses 配合 open_file_cache_valid 时,nginx 才可能在特定条件下复用已打开的 fd。但即使如此:
- fd 不是永久驻留的——它只在 cache entry 处于 “active” 状态且被多次命中(≥
min_uses)、且未过期/未失效时,才可能被保持并复用; - 一旦 cache entry 被标记为 inactive(例如长时间未被访问),或触发了失效检查(如
retest发现文件已变),nginx 会调用close(fd)并清空该 fd 字段; - 绝大多数场景下(尤其是静态文件服务未开启 fd 缓存优化),每次
open()后都会在请求结束前立即close(),fd 不进入持久缓存。
cache entry 的回收由双向链表 + LRU + 定时淘汰共同驱动
真正被缓存和回收的是 ngx_open_file_cache_node_t 结构体(即一个文件路径对应的元信息条目)。其回收逻辑集中在 ngx_open_file_cache_expire() 函数中,该函数被周期性调用(由 cache 的 inactive 时间控制):
- 遍历 cache 的
expire_queue(按最后访问时间排序的双向链表); - 跳过仍处于 active 状态(被当前请求引用)的节点;
- 对超时(
last_access + inactive)的节点:先 close fd(如果 fd 有效),再释放内存; - 若 cache 已满(节点数 ≥
max),则强制从 expire_queue 尾部逐个淘汰最久未用项,无论是否超时。
失效检测(invalidate)是主动回收的关键触发点
除了被动超时,以下操作会立即标记 cache entry 为无效,并在下次 expire 时回收:
- 收到
SIGHUP或USR1信号(重载配置或重新打开日志)时,整个 cache 被清空; - 配置了
open_file_cache_errors且某次stat()或open()失败后,对应 entry 被标记 invalid; - 配置了
open_file_cache_retest,并在后续检查中发现文件 mtime / size / existence 变化,则置 invalid; - 文件系统事件(如 inotify)未被 nginx 使用——nginx 不依赖内核通知,全靠周期性 stat 主动探测。
fd 复用仅发生在极少数优化路径中
真正保留 fd 并复用的情形需同时满足:
-
open_file_cache开启; -
open_file_cache_min_uses N(N ≥ 2); -
open_file_cache_valid T(T 足够长); - 同一文件在 T 内被至少 N 次请求命中,且中间未发生 stat 失败或 retest 不一致;
- 此时 node->fd 会被保持,后续
open()调用可跳过系统调用,直接 dup(fd) 或直接使用(见ngx_open_cached_file中的if (of->fd != NGX_INVALID_FILE)分支)。
这种优化对高并发重复读同一静态资源(如 favicon.ico、common.css)有一定收益,但不是通用行为。










