一、原理
nginx的锁是基于共享内存实现的,这点跟redis中利用一个存储(也就是一个键值对)来实现锁的原理是一致的,每一项操作通过检查锁对象的lock域是否为0,来判断能否获取锁并尝试获取锁。
二、锁的类定义
1. 类定义
1 //锁的定义 2 typedef struct { 3 #if (NGX_HAVE_ATOMIC_OPS) 4 ngx_atomic_t *lock; //如果支持原子锁的话,那么使用它 5 #if (NGX_HAVE_POSIX_SEM) 6 ngx_atomic_t *wait; 7 ngx_uint_t semaphore; 8 sem_t sem; 9 #endif 10 #else 11 ngx_fd_t fd; //不支持原子操作的话就使用文件锁来实现 12 u_char *name; 13 #endif 14 ngx_uint_t spin; //这是自旋锁么? 15 } ngx_shmtx_t;
2. 框架初始化时机
ngx_event_core_module中调用init函数来初始化这段锁的共享内存
1 /*后面将会创建size大小的共享内存,这块共享内存将被均分成三段, 2 分别供ngx_accept_mutex、ngx_connection_counter、 3 ngx_temp_number 使用。*/ 4 /* cl should be equal to or greater than cache line size */ 5 cl = 128; 6 size = cl /* ngx_accept_mutex */ 7 + cl /* ngx_connection_counter */ 8 + cl; /* ngx_temp_number */ 9 10 //共享内存的初始化 11 shm.size = size; 12 shm.name.len = sizeof("nginx_shared_zone"); 13 shm.name.data = (u_char *) "nginx_shared_zone"; 14 shm.log = cycle->log; 15 16 if (ngx_shm_alloc(&shm) != NGX_OK) { //为共享内存分配内存空间 17 return NGX_ERROR; 18 } 19 20 shared = shm.addr; //获取共享内存的地址 21 22 ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; //存放互斥量内存地址的指针 23 ngx_accept_mutex.spin = (ngx_uint_t) -1; //初始化自旋锁的初值为-1 24 25 if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared, //如果支持原子操作的话,这个就很简单了,就直接将内存地址分配过去就行了 26 cycle->lock_file.data) 27 != NGX_OK) 28 { 29 return NGX_ERROR; 30 } 31 32 ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); //ngx_connection_counter为其分配共享内存的内存空间 33 34 (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1); 35 36 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, 37 "counter: %p, %d", 38 ngx_connection_counter, *ngx_connection_counter); 39 40 ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl); //ngx_temp_number的内存空间
三、基本操作
1. try_lock
lock域是否为0 -> 尝试获取锁 (获取锁的表现就是在lock存入了该进程的pid)-> 返回结果
1 //尝试获取锁,原子的方式 2 ngx_uint_t 3 ngx_shmtx_trylock(ngx_shmtx_t *mtx) 4 { 5 return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)); 6 }
2. lock
循环获取锁
1 //阻塞的方式获取锁 2 void 3 ngx_shmtx_lock(ngx_shmtx_t *mtx) 4 { 5 ngx_uint_t i, n; 6 7 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock"); 8 //一个死循环,不断的去看是否获取了锁,直到获取了之后才退出 9 for ( ;; ) { 10 //如果获取了锁,那么就可以直接返回了 11 if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { 12 return; 13 } 14 //如果cpu的数量大于一 15 if (ngx_ncpu > 1) { 16 for (n = 1; n < mtx->spin; n <<= 1) { 17 for (i = 0; i < n; i++) { 18 ngx_cpu_pause(); 19 } 20 21 if (*mtx->lock == 0 22 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) 23 { 24 return; 25 } 26 } 27 } 28 29 ngx_sched_yield(); 30 } 31 }
3.unlock
是否拥有锁 -> 释放锁
1 //释放锁 2 void 3 ngx_shmtx_unlock(ngx_shmtx_t *mtx) 4 { 5 if (mtx->spin != (ngx_uint_t) -1) { 6 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock"); 7 } 8 9 if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) { 10 ngx_shmtx_wakeup(mtx); 11 } 12 }
四、用锁解决子进程的惊群现象
尝试获取锁 -> 成功 -> 已持有已把监听端口添加到epoll,返回NGX_OK,未添加则添加到epoll中并设置为持有
尝试获取锁 -> 失败 -> 若以前获取过,需要取消监听
1 //尝试获取锁,如果获取了锁,那么还要将当前监听端口全部注册到当前worker进程的epoll当中去 2 ngx_int_t 3 ngx_trylock_accept_mutex(ngx_cycle_t *cycle) 4 { 5 if (ngx_shmtx_trylock(&ngx_accept_mutex)) { //尝试获取互斥锁 6 7 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, 8 "accept mutex locked"); 9 //如果本来已经获得锁,则直接返回Ok 10 if (ngx_accept_mutex_held 11 && ngx_accept_events == 0 12 && !(ngx_event_flags & NGX_USE_RTSIG_EVENT)) 13 { 14 return NGX_OK; 15 } 16 //到达这里,说明重新获得锁成功,因此需要打开被关闭的listening句柄,调用ngx_enable_accept_events函数,将监听端口注册到当前worker进程的epoll当中去 17 if (ngx_enable_accept_events(cycle) == NGX_ERROR) { 18 ngx_shmtx_unlock(&ngx_accept_mutex); 19 return NGX_ERROR; 20 } 21 22 ngx_accept_events = 0; 23 ngx_accept_mutex_held = 1; //表示当前获取了锁 24 25 return NGX_OK; 26 } 27 28 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, 29 "accept mutex lock failed: %ui", ngx_accept_mutex_held); 30 //这里表示的是以前曾经获取过,但是这次却获取失败了,那么需要将监听端口从当前的worker进程的epoll当中移除,调用的是ngx_disable_accept_events函数 31 if (ngx_accept_mutex_held) { 32 if (ngx_disable_accept_events(cycle) == NGX_ERROR) { 33 return NGX_ERROR; 34 } 35 36 ngx_accept_mutex_held = 0; //表示当前并没有获取锁 37 } 38 39 return NGX_OK; 40 }
参考文献:
https://www.cnblogs.com/549294286/p/6058811.html