nginx通过非阻塞socket、epoll/kqueue事件驱动和内核协同机制实现高效i/o:全程不阻塞、不轮询、不空转,cpu仅处理就绪事件,等待由内核接管。

它不靠“让 CPU 等待 IO”,而是让 CPU 只在 IO 真正就绪时才介入——所有等待动作由内核接管,Nginx worker 进程全程保持运行状态,不挂起、不空转、不轮询。
非阻塞 socket 是前提
Nginx 从 accept 新连接开始,就把每个 socket 设为 O_NONBLOCK 模式。这意味着:
- recv() 没数据可读时,立刻返回 EAGAIN,而不是卡住进程
- send() 内核缓冲区满时,只发能发的部分,剩余数据暂存本地 buffer,不等写完再返回
- 整个 I/O 流程中,没有一次系统调用会让 worker 进程进入睡眠或阻塞状态
epoll/kqueue 让内核代劳监听
worker 进程不自己遍历所有连接看“有没有数据”,而是把监听任务交给内核:
- 调用 epoll_wait()(Linux)或 kqueue()(BSD/macOS),传入超时时间,然后交出控制权
- 内核在有事件(如可读、可写、断连)时才唤醒 Nginx,并返回就绪事件列表
- 避免了 select/poll 的全量 fd 扫描和用户态/内核态间重复拷贝,时间复杂度接近 O(1)
事件循环驱动分阶段处理
每个请求的生命周期被拆成小步骤,靠状态机推进,CPU 始终有事可做:
- 收到新连接,立即 accept,尽可能一次收多个,减少事件触发频次
- 解析 HTTP 头时,不等整个 body 到达,边收边解析,提前准备转发
- 响应发送未完成,自动注册 write 事件,下次 epoll_wait 返回后再续发
- 空闲连接不消耗 CPU,超时由红黑树统一管理,无需额外线程轮询
协同内核机制进一步压缩等待开销
减少 CPU 等待不只是事件模型的事,还依赖与内核能力的深度配合:
- sendfile() 实现零拷贝:文件内容直接从页缓存送入 socket buffer,跳过用户态内存拷贝,省掉两次上下文切换
- TCP_CORK / TCP_NOPUSH 合并小包,降低 write() 系统调用次数
- keepalive 复用 TCP 连接,避免频繁握手挥手带来的协议栈等待
- 异步 DNS 解析、缓冲日志、静态文件走内核页缓存,全程避开阻塞点
本质上,Nginx 把“等待”从 CPU 时间里彻底剥离出来,变成内核的后台任务;CPU 只负责处理“已经发生”的事,而不是守着连接干等。











