Nginx父子进程通信采用“共享内存+信号+进程协作”模式,主进程预分配并初始化共享内存,worker进程fork继承映射;通过ngx_shmtx_t自旋锁实现无锁化原子操作;信号仅作通知,worker在事件循环中安全读取共享状态。

Nginx 的父子进程通信不依赖传统 IPC(如管道、消息队列),而是以“共享内存 + 信号 + 进程协作”为主干,核心目标是轻量、高效、无锁化。它没有复杂的跨进程消息传递,而是通过预分配的共享内存区域 + 原子操作 + 信号通知,实现主进程对工作进程的控制与状态同步。
共享内存的创建与映射
Nginx 在 master 进程启动阶段调用 ngx_shm_alloc 创建共享内存段(底层通常使用 shm_open + mmap,或 posix_memalign 模拟,取决于配置和平台)。该内存段被 mmap 到 master 和所有 worker 进程的地址空间中,地址一致,内容可见。
关键点:
- 共享内存结构由 Nginx 模块自行定义(如
ngx_slab_pool_t管理 slab 分配器,ngx_shmtx_t封装锁) - master 进程负责初始化共享内存池(包括 slab 初始化、互斥锁字段置零等)
- worker 进程 fork 后自动继承 mmap 映射,无需重新 open 或 mmap
基于原子操作的共享内存锁(ngx_shmtx_t)
Nginx 不使用系统级互斥锁(如 pthread_mutex_t),而采用自旋锁(spinlock)语义 + 原子指令实现轻量级共享内存锁,结构体为 ngx_shmtx_t:
-
lock:指向共享内存中的一个
uint32_t变量,作为锁标志(0 表示空闲,1 表示已加锁) - log:日志上下文,用于错误输出
-
spin:自旋次数(仅在支持原子 CAS 的平台启用,如 x86 的
cmpxchg)
加锁过程(ngx_shmtx_lock)本质是循环执行原子比较并交换(CAS):
- 尝试将 lock 从 0 改为 1;成功则获得锁
- 失败则短暂自旋(若配置了 spin),之后可能让出 CPU(
sched_yield)或进入休眠(依赖ngx_shmtx_wait机制) - 解锁即原子写入 0(
ngx_shmtx_unlock)
注意:该锁仅保证临界区互斥,不解决进程调度或信号中断问题,因此临界区必须极短(如更新计数器、修改位图、增删连接节点)。
父子进程协作:信号驱动 + 共享内存状态同步
master 与 worker 之间不主动推送数据,而是通过信号触发 worker 主动读取共享内存中的新指令或配置:
- master 修改共享内存中的配置结构(如重载时更新
ngx_cycle_t*相关字段)后,向所有 worker 发送SIGUSR2(重载)、SIGHUP(平滑重启)、SIGQUIT(优雅退出)等信号 - worker 在信号处理函数中仅设置标志位(如
ngx_reloading = 1),不执行实际逻辑 - worker 主循环(event loop)每次迭代前检查这些标志,并在安全时机(如 accept 完成后、无活跃请求时)读取共享内存中的新配置、重建连接池、切换 cycle 等
这种“信号通知 + 轮询检查 + 用户态协同”的方式,避免了信号处理函数中执行复杂操作的风险,也规避了锁竞争热点。
典型应用:限流模块(limit_req)与连接计数
以 ngx_http_limit_req_module 为例说明共享内存锁的实际使用:
- 所有 worker 共用一块共享内存存储令牌桶状态(key → last, excess, nodelay 等)
- 每次请求到来,worker 先调用
ngx_shmtx_lock(&shpool->mutex)获取锁 - 在临界区内查找 key、更新时间戳、计算令牌、判断是否限流
- 完成后立即
ngx_shmtx_unlock,释放锁
由于锁粒度细(按 key hash 分桶可进一步分拆)、持有时间短,整体吞吐不受明显影响。Nginx 甚至提供 limit_req_zone ... zone=name:10m 显式指定共享内存大小,体现其核心地位。
不复杂但容易忽略:Nginx 的“通信”本质是共享状态 + 协同行为,不是消息收发;锁只是保障共享状态修改安全的工具,真正的协调逻辑在信号响应与事件循环中完成。










