一、原理

  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

nginx的锁的更多相关文章

  1. nginx自旋锁

    #include <stdio.h> #include <stdint.h> #include <unistd.h> /* typedef unsigned lon ...

  2. Nginx(八): 观进程锁的实现

    前面的nginx系列讲解了nginx很多通用概念,流程,以及核心的http模块的一些实现.应该说大体上对nginx已经不再陌生和神秘. 今天我们不看全局,只看一个非常非常小的细节:nginx是多进程并 ...

  3. 一、Nginx配置文件详解

    配置文件介绍 主要有两部分:分别是 main:主体部分 http{}:虚拟主机配置部分 配置指令主要以分号结尾:配置语法:directive value1 [value2 ....] 支持使用的变量 ...

  4. Nginx实用教程(二):配置文件入门

    Nginx配置文件结构 nginx配置文件由指令(directive)组成,指令分为两种形式,简单指令和区块指令. 一条简单指令由指令名.参数和结尾的分号(;)组成,例如: listen backlo ...

  5. 源码编译安装nginx

    安装依赖软件 1.安装编译工具gcc gcc是一个开源编译器集合,用于处理各种各样的语言:C.C++.Java.Ada等,在linux世界中是最通用的编译器,支持大量处理器:x86.AMD64.Pow ...

  6. Nginx实践01-ngnix编译安装-测试

    1.下载nginx安装包 下载地址:http://nginx.org/en/download.html(里面有nginx各个版本) 解压到指定目录: 解压出来的目录简单介绍: src:软件的所有源代码 ...

  7. nginx(4)

    目录 一.安装配置 1.安装 2.配置文件 3.测试和启动 二.功能 1.虚拟主机 1.1 基于IP 1.2 基于域名 1.3 基于端口 2.访问控制 3.用户认证 4.文件共享 5.文件别名 6.状 ...

  8. 11 . Nginx核心原理讲解

    应用场景优缺点 应用场景 // 1.静态请求 // 2.反向代理 // 3.负载均衡 // 4.资源缓存 // 5.安全防护 // 6.访问限制IP // 7.访问认证 /* 核心主要是以下三个应用: ...

  9. 【Linux】【Services】【Web】Nginx基础

    1. 概念 1.1. 消息通知机制:同步synchronous,异步asynchronous 同步:等待对方返回信息 异步:被调用者通过状态.通知或回调通知调用者 状态:调用者每隔一段时间就需要检查一 ...

随机推荐

  1. MATLA总结三

    1.将分数表示转换为小数表示: vpa( ******(表达式或者变量), n(小数点后几位)) 2.将一个式子中的变量带入具体的值: subs(  y, x, [2,3,4] ) 3.用工具箱确定阶 ...

  2. Tensorflow图像处理以及数据读取

    关于tensoflow的图像的处理,看到了一篇文章,个人觉得不错.https://blog.csdn.net/weiwei9363/article/details/79917942

  3. for循环 && for-each

    Effective Java 第46条for-each循环优先于传统for循环 问题: 使用for循环来遍历集合或者是数组可以借助迭代器和索引变量.但是,如果出现循环嵌套的时候很容易引起混乱. 例如: ...

  4. Spring 配置 定时任务

    官档地址:https://docs.spring.io/spring/docs/5.1.4.RELEASE/spring-framework-reference/integration.html#sc ...

  5. MongoDB基本语法

    建立连接 client = pymongo.MongoClient() 新建数据库 db = client["db_name"] 新建表 tble=db["table_n ...

  6. 【笔记】Python基础六:模块module介绍及常用模块

    一,module模块和包的介绍 1,在Python中,一个.py文件就称之为一个模块(Module). 2,使用模块的好处? 最大的好处是大大提高了代码的可维护性 其次,编写代码不必从零开始,我们编写 ...

  7. Heroku发布前准备

    group :development, :test do gem 'byebug', platform: :mri gem 'sqlite3', '~> 1.3.13' end group :p ...

  8. 洛谷P4051 [JSOI2007]字符加密 后缀数组

    题目链接:https://www.luogu.org/problemnew/show/P4051 思路:我们联想求后缀数组sa的过程,发现我们在求y数组的时候(第二关键字,下标为第二关键字的排位,值为 ...

  9. PCA降维参数介绍

    https://www.cnblogs.com/pinard/p/6243025.html#undefined

  10. JavaSE基础知识(5)—面向对象(5.7 final关键字)

    一.说明 final属于一种修饰符,可以用于修饰类和属性.方法.局部变量 二.特点 1.修饰类 该类不能被继承,如String.Integer等 2.修饰方法 该方法不能被重写 3.修饰变量(属性和局 ...