一、原理

  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. foreach和List.Foreach 退出循环相关问题

    foreach: continue;:退出本次循环 break;:退出循环 return;:退出循环 List.Foreach: return;:退出本次循环 小结:list.Foreach中不能退出 ...

  2. 大数据入门到精通16--hive 的条件语句和聚合函数

    一.条件表达 case when ... then when .... then ... when ... then ...end select film_id,rpad(title,20," ...

  3. pandas的简单使用

    pandas可以对数据进行整理分析 因为要对excel中的源数据进行分组和处理,所以想到用pandas来处理.试用过确实比自己去读写快捷很多 (实际pandas底层也是用xlrd,xlwt两个第三方包 ...

  4. 项目总结21:项目总结21:input实现多图上传(FormData)(上传OSS并保存数据库)

    项目总结21:input实现多图上传(FormData)(上传OSS并保存数据库) 备注:本案例,作为Demo,包含少量的项目业务逻辑,input多图上传的逻辑是完整的: 不废话直接上代码 1-前端标 ...

  5. Spring常用注解总结(2)

    @Autowired "自动填装",作用是为了消除代码JAVA代码里面的getter/setter与bean属性中的property. @Autowired默认按类型匹配的方式,在 ...

  6. JAVA EE 环境配置——JAVA8 下载安装和 Eclipse EE的下载安装

    说明:我的电脑是win10 64位操作系统 步骤1:下载Java8 浏览器输入www.oracle.com,点击 Downloads,选择 Java ,选择 Java (JDK) for Develo ...

  7. 面向对象的 __slots__

    优点: 1. __slots__ 能够减小内存的占用,限制对象只能有这几个属性,再加属性会报错 . 副作用: 1. 设置了__slots__之后,实例对象就没有__dict__了 2. __slots ...

  8. 解决代理池的问题AttributeError: 'int' object has no attribute 'items'

    https://blog.csdn.net/mygodit/article/details/86689127

  9. spark 线性回归算法(scala)

    构建Maven项目,托管jar包 数据格式 //0.fp_nid,1.nsr_id,2.gf_id,2.hydm,3.djzclx_dm,4.kydjrq,5.xgrq,6.je,7.se,8.jsh ...

  10. filter 全局和局部过滤器

    1,局部过滤器 2,全局过滤器 使用方法相同,在花括号中使用过滤器名或者v-bind中使用