Nginx采用共享内存+原子操作实现无锁协同,避免传统进程锁开销;主进程预分配共享内存区,Worker通过mmap映射并用CAS、fetch-and-add等原子指令安全访问计数器与令牌桶。

Nginx 的 Worker 进程之间不共享内存,彼此独立运行,因此传统锁(如互斥量、信号量)在跨进程同步中开销大、易引发阻塞。Nginx 选择绕过锁机制,转而依赖 共享内存 + 原子操作 实现高效、低延迟的无锁化协同——这不是“完全不用同步”,而是用更轻量、更可控的原子指令替代内核级锁。
共享内存是无锁协作的前提
Nginx 启动时,主进程会预先分配一块共享内存区域(通过 shm 指令配置,如 zone=one:10m),所有 Worker 进程通过 mmap 映射到各自地址空间。这块内存里的数据(如连接计数、限流令牌桶、缓存状态等)可被所有 Worker 直接读写。
但直接读写会引发竞态:两个 Worker 同时对一个计数器执行 ++,可能只生效一次。解决方法不是加锁,而是用原子操作保障单个读-改-写操作的不可分割性。
核心原子操作:CAS 与 fetch-and-add
Nginx 封装了平台相关的原子指令(位于 src/core/ngx_atomic.h),关键接口包括:
-
ngx_atomic_cmp_set(p, old, new):对应 CAS(Compare-And-Swap)。仅当 *p == old 时,将 *p 设为 new,并返回成功;否则失败。用于实现自旋锁、无锁栈、乐观更新等。 -
ngx_atomic_fetch_add(p, add):原子地将 *p 加上 add,并返回加之前的旧值。常用于统计类场景,如ngx_stat_active连接数增减。 -
ngx_atomic_fetch_add在 x86 上通常编译为lock xadd指令,在 ARM 上为ldrex/strex循环,均由硬件保证原子性。
典型无锁应用:连接计数与限流
以连接统计为例:ngx_stat_active 是一个全局原子变量。Worker 接收新连接时调用:
关闭连接时调用:
ngx_atomic_fetch_add(&ngx_stat_active, -1);无需加锁,无上下文切换,毫秒级响应。类似逻辑也用于漏桶/令牌桶算法中的令牌发放与消耗——每次取令牌前用 CAS 尝试更新桶中剩余数量,失败则说明令牌不足,直接拒绝请求。
注意事项:原子操作 ≠ 万能,需配合合理设计
- 原子操作仅保障单个变量的读-改-写原子性,无法自动保护多字段逻辑一致性(如同时更新连接数和字节数,需自行设计复合结构或分阶段更新)。
- CAS 可能遭遇 ABA 问题,但 Nginx 在多数统计与限流场景中不敏感(计数值单调变化,或有时间戳辅助)。
- 高争用下 CAS 自旋会浪费 CPU,Nginx 通常通过业务设计降低争用(如按连接哈希分散到不同共享桶,而非集中更新单个计数器)。

















