首页 > 网站 > Nginx > 正文

Nginx中accept锁的机制与实现详解

2024-08-30 12:24:39
字体:
来源:转载
供稿:网友

前言

nginx采用多进程的模,当一个请求过来的时候,系统会对进程进行加锁操作,保证只有一个进程来接受请求。

本文基于Nginx 0.8.55源代码,并基于epoll机制分析

1. accept锁的实现

1.1 accpet锁是个什么东西

提到accept锁,就不得不提起惊群问题。

所谓惊群问题,就是指的像Nginx这种多进程的服务器,在fork后同时监听同一个端口时,如果有一个外部连接进来,会导致所有休眠的子进程被唤醒,而最终只有一个子进程能够成功处理accept事件,其他进程都会重新进入休眠中。这就导致出现了很多不必要的schedule和上下文切换,而这些开销是完全不必要的。

而在Linux内核的较新版本中,accept调用本身所引起的惊群问题已经得到了解决,但是在Nginx中,accept是交给epoll机制来处理的,epoll的accept带来的惊群问题并没有得到解决(应该是epoll_wait本身并没有区别读事件是否来自于一个Listen套接字的能力,所以所有监听这个事件的进程会被这个epoll_wait唤醒。),所以Nginx的accept惊群问题仍然需要定制一个自己的解决方案。

accept锁就是nginx的解决方案,本质上这是一个跨进程的互斥锁,以这个互斥锁来保证只有一个进程具备监听accept事件的能力。

实现上accept锁是一个跨进程锁,其在Nginx中是一个全局变量,声明如下:

ngx_shmtx_t   ngx_accept_mutex;

这是一个在event模块初始化时就分配好的锁,放在一块进程间共享的内存中,以保证所有进程都能访问这一个实例,其加锁解锁是借由linux的原子变量来做CAS,如果加锁失败则立即返回,是一种非阻塞的锁。加解锁代码如下:

static ngx_inline ngx_uint_t             ngx_shmtx_trylock(ngx_shmtx_t *mtx)           {                     return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid));  }                                        #define ngx_shmtx_lock(mtx) ngx_spinlock((mtx)->lock, ngx_pid, 1024)                       #define ngx_shmtx_unlock(mtx) (void) ngx_atomic_cmp_set((mtx)->lock, ngx_pid, 0)

可以看出,调用ngx_shmtx_trylock失败后会立刻返回而不会阻塞。

1.2 accept锁如何保证只有一个进程能够处理新连接

要解决epoll带来的accept锁的问题也很简单,只需要保证同一时间只有一个进程注册了accept的epoll事件即可。
Nginx采用的处理模式也没什么特别的,大概就是如下的逻辑:

尝试获取accept锁
if 获取成功:
 在epoll中注册accept事件
else:
 在epoll中注销accept事件
处理所有事件
释放accept锁

当然这里忽略了延后事件的处理,这部分我们放到后面讨论。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表