Nginx 依赖内核就绪队列实现高效 I/O 多路复用,epoll_wait 仅返回已就绪 fd,不判断连接状态;Nginx 基于自身状态机调度 accept、recv、send 等操作,并在 ET 模式下一次性处理完事件。

Nginx 使用 epoll_wait 检索活跃连接,本质是依赖 Linux 内核维护的就绪队列,而非轮询扫描所有连接。
内核就绪队列是核心数据结构
当 socket 进入可读(如收到 TCP 数据包)、可写(发送缓冲区有空闲)或异常状态时,内核会将对应文件描述符(fd)加入该 epoll 实例关联的就绪队列。这个队列由红黑树 + 双向链表实现,插入/删除时间复杂度接近 O(1),查找就绪事件为 O(1)。
调用 epoll_wait 时,内核直接从就绪队列头部取一批已就绪的 fd(数量不超过传入的数组长度),填充到用户提供的 events 数组中,无需遍历全部监听的 fd。
epoll\_wait 不做连接状态判断
epoll_wait 只返回“哪些 fd 就绪了”,不关心连接是否断开、是否超时、是否已完成握手。它只反映内核协议栈当前的 I/O 状态:
- 可读事件可能意味着:新连接到达(listen fd)、数据到达(client fd)、对端关闭连接(recv 返回 0)
- 可写事件通常表示:TCP 发送缓冲区有空间(但不保证数据已送达对端)
- EPOLLERR 或 EPOLLHUP 表示连接异常,需主动调用
recv或getsockopt确认具体原因
Nginx 在事件就绪后执行精细处理
收到 epoll_wait 返回的就绪事件后,Nginx 并非直接读写,而是按连接状态机调度后续动作:
- 对 listen fd:调用
accept()接收新连接,并设置为 ET 模式(边缘触发),避免重复通知 - 对 client fd:检查
ngx_connection_t的read->ready/write->ready标志,结合recv_buffer和send_buffer状态决定是否调用recv、send或进入延迟关闭流程 - 对已关闭或错误连接:清理定时器、释放内存池、关闭 fd、重置连接结构体,防止残留资源占用
ET 模式与一次性通知机制
Nginx 默认使用 EPOLLET(边缘触发)。这意味着:
- 仅当 fd 状态从“未就绪”变为“就绪”时,
epoll_wait才通知一次 - 若一次未读完全部数据(比如 recv 返回 EAGAIN),不会再次通知,必须持续读直到返回 EAGAIN
- 这要求 Nginx 必须在单次就绪处理中尽量完成 I/O,否则可能漏事件;也减少了内核和用户态间不必要的上下文切换
不复杂但容易忽略:epoll_wait 是被动通知接口,真正决定连接如何响应的,是 Nginx 自己的状态管理和事件驱动调度逻辑。

















