nginx每个worker进程独享一个epoll实例,master fork后各worker在init中调用epoll_create1创建专属epoll fd并初始化事件数组,4个worker即有4个隔离的epoll实例,各自处理分配到的连接fd,无锁竞争、无事件混杂,全程封闭在单个worker内。

Nginx 的 worker_processes 本身不直接“使用” epoll,而是每个 worker 进程在初始化后,独立创建并管理自己的 epoll 实例,再通过事件循环驱动 I/O 处理。epoll 是底层机制,worker 进程是执行主体——二者是“每个进程配一套 epoll”的关系,不是全局共享或跨进程复用。
每个 worker 进程独享一个 epoll 实例
master 进程 fork 出多个 worker 后,每个 worker 在 ngx_worker_process_init() 中调用 ngx_epoll_init(),执行:
- 调用
epoll_create1(0)创建专属的 epoll fd(保存在ngx_cycle->files) - 分配独立的就绪事件数组(默认 512 项),用于后续
epoll_wait()接收结果 - 将
ngx_epoll_actions赋给全局ngx_event_actions,使本进程所有事件操作(add/del/mod/wait)都走 epoll 分支
这意味着:4 个 worker 就有 4 个互不干扰的 epoll 实例,各自监听自己分到的连接 fd,不存在锁竞争或事件混杂。
worker 循环中统一调用 epoll_wait 等待事件
每个 worker 进入主循环 ngx_worker_process_cycle() 后,反复执行 ngx_process_events_and_timers(),该函数最终调用 ngx_epoll_process_events():
- 以指定超时时间(来自红黑树定时器)调用
epoll_wait(ep, events, nevents, timer) - 返回的就绪事件全部属于本 worker 管理的 fd(包括监听 socket、已建立连接、channel socket 等)
- 遍历每个就绪事件,从
ev->data.ptr恢原出ngx_event_t*,再根据revents调用对应 handler(如rev->handler处理读,wev->handler处理写)
连接分配与事件注册由 worker 自主完成
操作系统内核(如 Linux 的 SO_REUSEPORT 或 accept 队列分发)把新连接分配给某个 worker,该 worker 独立完成后续全部事件管理:
- 监听 socket 在
ngx_event_process_init()中注册EPOLLIN,等待新连接 -
accept()成功后,新 client fd 立即被ngx_add_conn()注册为EPOLLIN | EPOLLET(边缘触发) - 需要发送响应时,仅当发送缓冲区满、
send()返回EAGAIN,才临时注册EPOLLOUT;数据发完立刻移除,避免持续触发
整个过程不依赖其他 worker,也不跨进程同步状态——连接生命周期、事件注册/注销、读写调度,全部封闭在单个 worker 内。
多 worker + 多 epoll 提升并发吞吐的关键点
这种设计规避了单线程瓶颈,也避免了多线程锁开销,核心优势在于:
-
CPU 绑定友好:配合
worker_cpu_affinity,每个 worker 可绑定到独立 CPU 核心,epoll_wait 和事件处理全在本地 cache 运行 - 连接隔离:一个 worker 崩溃不影响其他 worker 正在处理的连接
- 无共享事件队列:不需要跨进程传递事件或协调 epoll_wait,消除同步开销
-
ET 模式可控性高:每个 worker 自行保证非阻塞循环读写至
EAGAIN,适配高并发短连接与长连接混合场景











