Nginx 通过 epoll_wait() 被动接收内核通知的就绪 fd,而非主动扫描;其事件循环依据返回的就绪事件调用对应 handler 处理监听或客户端连接,时间复杂度与总连接数无关。

Nginx 使用 epoll_wait() 获取就绪的文件描述符(fd),但它**并不主动“检索”活跃连接**,而是由内核通过 epoll 机制**被动通知**哪些连接已就绪——这是理解其高效性的关键。
epoll_wait 的作用本质是“等待通知”,不是“扫描查询”
Linux 内核在 socket 状态变化(如收到数据、可写、断开等)时,会将对应 fd 加入 epoll 实例的就绪队列。Nginx 调用 epoll_wait() 时,只是从该队列中取出已就绪的 fd 列表,不遍历所有监听或已建立的连接。这意味着:
- 时间复杂度为 O(1) 到 O(就绪事件数),与总连接数无关
- 没有轮询开销,避免了 select/poll 的全量 fd 遍历
- 内核维护红黑树 + 就绪链表,插入/删除/通知均高效
活跃连接如何被“识别”并交由 Nginx 处理
Nginx 在初始化阶段已将监听 socket 和所有 accept 后的客户端 socket 注册到 epoll 实例(epoll_ctl(EPOLL_CTL_ADD))。当 epoll_wait() 返回一个就绪 fd 时,Nginx 根据 fd 查找对应的 ngx_connection_t 结构体,进而:
- 若为监听 fd → 执行
accept(),建立新连接,并注册新 fd 到 epoll - 若为客户端 fd 且可读 → 调用
recv()读取请求头/体,触发 HTTP 解析流程 - 若为客户端 fd 且可写 → 将响应数据发送出去,可能关闭连接或进入长连接等待
真正决定“是否活跃”的是内核事件,不是 Nginx 主动判断
所谓“活跃”,完全由内核根据网络栈状态判定:
- TCP 收到新数据包 → 触发 EPOLLIN 事件
- 发送缓冲区有空闲 → 触发 EPOLLOUT 事件(对非阻塞 socket)
- 对端 FIN 或 RST 到达 → 触发 EPOLLIN(read 返回 0)或 EPOLLERR
- 超时、错误、半关闭等也产生对应事件
Nginx 不做心跳探测或定时检查;它只响应内核推送的事件。长连接的保持,依赖于客户端是否在 keepalive timeout 内发起新请求——此时新请求到达会再次触发 EPOLLIN,连接自然延续。
epoll_wait 的返回值直接驱动事件循环主干
Nginx worker 进程的核心循环大致如下:
- 调用
epoll_wait()阻塞等待(可设超时,用于定时任务检查) - 遍历返回的就绪事件数组,对每个 event 执行
rev->handler或wev->handler - handler 是函数指针,例如
ngx_http_process_request_line或ngx_http_writer - 处理完一批事件后,立即再次进入
epoll_wait(),无空转、无遗漏
这个模型让单个 worker 能高效复用一个线程服务数万并发连接,前提是:所有 socket 必须设为非阻塞,且所有 I/O 操作必须适配事件驱动逻辑。










