一、原理

  nginx的锁是基于共享内存实现的,这点跟redis中利用一个存储(也就是一个键值对)来实现锁的原理是一致的,每一项操作通过检查锁对象的lock域是否为0,来判断能否获取锁并尝试获取锁。

二、锁的类定义

1. 类定义

 //锁的定义
typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
ngx_atomic_t *lock; //如果支持原子锁的话,那么使用它
#if (NGX_HAVE_POSIX_SEM)
ngx_atomic_t *wait;
ngx_uint_t semaphore;
sem_t sem;
#endif
#else
ngx_fd_t fd; //不支持原子操作的话就使用文件锁来实现
u_char *name;
#endif
ngx_uint_t spin; //这是自旋锁么?
} ngx_shmtx_t;

ngx_shmtx_t

2. 框架初始化时机

  ngx_event_core_module中调用init函数来初始化这段锁的共享内存 

 /*后面将会创建size大小的共享内存,这块共享内存将被均分成三段,
分别供ngx_accept_mutex、ngx_connection_counter、
ngx_temp_number 使用。*/
/* cl should be equal to or greater than cache line size */
cl = ;
size = cl /* ngx_accept_mutex */
+ cl /* ngx_connection_counter */
+ cl; /* ngx_temp_number */ //共享内存的初始化
shm.size = size;
shm.name.len = sizeof("nginx_shared_zone");
shm.name.data = (u_char *) "nginx_shared_zone";
shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) { //为共享内存分配内存空间
return NGX_ERROR;
} shared = shm.addr; //获取共享内存的地址 ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; //存放互斥量内存地址的指针
ngx_accept_mutex.spin = (ngx_uint_t) -; //初始化自旋锁的初值为-1 if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared, //如果支持原子操作的话,这个就很简单了,就直接将内存地址分配过去就行了
cycle->lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
} ngx_connection_counter = (ngx_atomic_t *) (shared + * cl); //ngx_connection_counter为其分配共享内存的内存空间 (void) ngx_atomic_cmp_set(ngx_connection_counter, , ); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"counter: %p, %d",
ngx_connection_counter, *ngx_connection_counter); ngx_temp_number = (ngx_atomic_t *) (shared + * cl); //ngx_temp_number的内存空间

init

三、基本操作

1. try_lock

  lock域是否为0 -> 尝试获取锁 (获取锁的表现就是在lock存入了该进程的pid)-> 返回结果 

 //尝试获取锁,原子的方式
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t *mtx)
{
return (*mtx->lock == && ngx_atomic_cmp_set(mtx->lock, , ngx_pid));
}

try_lock

2. lock

  循环获取锁

 //阻塞的方式获取锁
void
ngx_shmtx_lock(ngx_shmtx_t *mtx)
{
ngx_uint_t i, n; ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, , "shmtx lock");
//一个死循环,不断的去看是否获取了锁,直到获取了之后才退出
for ( ;; ) {
//如果获取了锁,那么就可以直接返回了
if (*mtx->lock == && ngx_atomic_cmp_set(mtx->lock, , ngx_pid)) {
return;
}
//如果cpu的数量大于一
if (ngx_ncpu > ) {
for (n = ; n < mtx->spin; n <<= ) {
for (i = ; i < n; i++) {
ngx_cpu_pause();
} if (*mtx->lock ==
&& ngx_atomic_cmp_set(mtx->lock, , ngx_pid))
{
return;
}
}
} ngx_sched_yield();
}
}

lock

3.unlock

  是否拥有锁 -> 释放锁 

 //释放锁
void
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
{
if (mtx->spin != (ngx_uint_t) -) {
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, , "shmtx unlock");
} if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, )) {
ngx_shmtx_wakeup(mtx);
}
}

unlock

四、用锁解决子进程的惊群现象

  尝试获取锁 -> 成功 -> 已持有已把监听端口添加到epoll,返回NGX_OK,未添加则添加到epoll中并设置为持有

  尝试获取锁 -> 失败 -> 若以前获取过,需要取消监听 

 //尝试获取锁,如果获取了锁,那么还要将当前监听端口全部注册到当前worker进程的epoll当中去
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
if (ngx_shmtx_trylock(&ngx_accept_mutex)) { //尝试获取互斥锁 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"accept mutex locked");
//如果本来已经获得锁,则直接返回Ok
if (ngx_accept_mutex_held
&& ngx_accept_events ==
&& !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
{
return NGX_OK;
}
//到达这里,说明重新获得锁成功,因此需要打开被关闭的listening句柄,调用ngx_enable_accept_events函数,将监听端口注册到当前worker进程的epoll当中去
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
ngx_shmtx_unlock(&ngx_accept_mutex);
return NGX_ERROR;
} ngx_accept_events = ;
ngx_accept_mutex_held = ; //表示当前获取了锁 return NGX_OK;
} ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"accept mutex lock failed: %ui", ngx_accept_mutex_held);
//这里表示的是以前曾经获取过,但是这次却获取失败了,那么需要将监听端口从当前的worker进程的epoll当中移除,调用的是ngx_disable_accept_events函数
if (ngx_accept_mutex_held) {
if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
return NGX_ERROR;
} ngx_accept_mutex_held = ; //表示当前并没有获取锁
} return NGX_OK;
}

ngx_trylock_accept_mutex

参考文献:

https://www.cnblogs.com/549294286/p/6058811.html

最新文章

  1. JavaMail发送邮件
  2. Android Studio一些常用快捷键及快捷键冲突解决
  3. HTML常用命名和CSS reset代码【收集总结】
  4. 第五篇 Integration Services:增量加载-Deleting Rows
  5. Codeforces Round #337 Alphabet Permutations
  6. c#操作Zip压缩文件
  7. 【数据结构和算法】 O(1)时间取得栈中的最大 / 最小元素值
  8. eclipse 中创建maven web项目
  9. Date对象
  10. AngularJs练习Demo16 ngRoute
  11. JS获得URL参数
  12. Django中的Python高级特性
  13. css实现垂直水平居中的方法(个数不限)?
  14. P1553 数字反转(升级版)(模拟)
  15. 深浅copy详解
  16. python中的变量和算数运算
  17. 【ES】学习1-入门使用
  18. python+selenium一:对浏览器的操作
  19. linux文件管理之管道与重定向
  20. 【leetcode】Path Sum2

热门文章

  1. web应用程序+HTTP协议
  2. 【笔记】Python基础六:模块module介绍及常用模块
  3. 18. 4Sum (JAVA)
  4. 转:Jmeter分布式测试
  5. 2018-2019-2 20165315《网络攻防技术》Exp6 信息搜集与漏洞扫描
  6. java中解析excel 批量插入数据库
  7. vim 中文乱码怎么解决
  8. mysql操作数据表中的记录1
  9. 2018-2019-2 《网络对抗技术》 EXP0 KALI安装 20165213 Week1
  10. testXSS &lt;img src=&quot;aa&quot; onerror=&quot;javascript:alert(&#39;XSS&#39;);&quot;/&gt;