nginx性能提升关键在于优化系统调用模式而非减少次数:通过epoll/kqueue实现就绪驱动、零遍历;非阻塞i/o+状态机确保单次调用处理最大有效数据;零拷贝与内存复用绕过用户态搬运;定时器策略避免盲目轮询。

Nginx 的异步事件驱动架构不是靠“减少系统调用次数”来提升性能,而是通过改变系统调用的模式和时机,把开销集中化、批量处理,并避免无效调用,从而显著降低单位请求的平均系统调用成本。
关键不在于“少调用”,而在于“调得准、调得省、不白调”。
用 epoll_wait 替代轮询式系统调用
传统 select/poll 每次都要把全部 fd 集合从用户态拷贝到内核态,时间复杂度 O(n),且返回后还需遍历所有 fd 判断就绪状态。
Nginx 默认启用 epoll(Linux)或 kqueue(BSD/macOS),其核心优势是:
-
一次注册,长期有效:fd 只需在初始化时调用
epoll_ctl(ADD)注册一次,后续无需重复传参 -
就绪驱动,零遍历:
epoll_wait()返回的是已就绪事件列表,worker 直接处理,跳过所有空转检查 - 内核维护就绪队列:避免每次调用都扫描全部连接,复杂度接近 O(1)
这相当于把“每毫秒问一遍:谁有事?”换成“等有人敲门,再开门”,大幅削减无意义的系统交互。
非阻塞 I/O + 状态机,让单次系统调用发挥最大效用
所有 socket 均设为 O_NONBLOCK,因此:
-
accept()可能一次收多个新连接(配合multi_accept on) -
recv()可能读到部分 HTTP 头,Nginx 不等待凑齐,而是保存当前解析状态,下次事件再续 -
send()若返回EAGAIN,不重试也不阻塞,而是把剩余数据挂起,等EPOLLOUT再触发
这样,每个 read/write 调用都尽可能带走最多有效数据,避免“调一次、读一字节、再调一次”的碎片化开销。
零拷贝与内存复用,绕过用户态搬运引发的隐式系统调用
很多系统调用开销其实来自数据搬运本身:
-
sendfile()让内核直接在文件页缓存和 socket buffer 间传输,省去read()+write()两次系统调用及用户态内存拷贝 -
ngx_connection_t结构体在启动时预分配整块内存池,连接复用时不 malloc/free,避免brk()/mmap()等内存管理调用 - 请求头、响应体 buffer 复用 slab 分配器,减少
get_free_pages()类内核内存分配调用
这些设计让数据流经路径上的系统调用数量被压缩到最小必要集。
定时器与唤醒策略避免盲目轮询
Nginx 默认不设 timer_resolution,意味着:
- 无网络事件时,
epoll_wait()可无限期阻塞(timeout = -1),CPU 完全释放 - 仅当有 socket 就绪、信号到达或定时器到期时才唤醒,杜绝空转唤醒
若强行配置 timer_resolution 1ms,反而会每毫秒强制唤醒并调用 epoll_wait() 和 clock_gettime(),增加系统调用频次和上下文切换——这不是优化,是代价交换。
不复杂但容易忽略











