文章9:Nginx accept互斥锁
2015-07-31 02:24
537 查看
欢迎转载,转载请注明出处/article/1389062.html
0.序
强烈推荐的文章是nginx中锁的设计以及惊群的处理,本文只是对文章中的内容进行了部分总结和部分解释。
Nginx之所以要采用Accept互斥锁就是为了避免惊群现象。
所谓惊群现象:指一个fd的事件被触发后,等候这个fd的所有线程/进程都被唤醒。虽然都被唤醒,但是只有一个会去响应。
Nginx使用的锁分为两种情况:1)一种是支持原子操作的情况,由NGX_H***E_ATOMIC_OPS这个宏来进行控制的。2)一种是不支持原子操作的情况,使用文件锁来实现。
用户空间进程间锁的实现的原理很简单,就是弄出一个让所有进程共享的东西,比如mmap的内容或文件,然后通过这个东西来控制进程的互斥。
1.Nginx锁的核心数据结构
Nginx中锁的核心数据结构,也就是Nginx使用哪个变量来控制进程的行为。
2.分析文件锁
由于本人对原子操作不了解,因此只能分析一下文件锁。Nginx使用文件锁,实际上就是APUE 14.3 记录锁中内容。
上锁:ngx_trylock_accept_mutex----------------->ngx_shmtx_trylock--------------->ngx_trylock_fd
删除锁:ngx_shmtx_unlock------------------------->ngx_unlock_fd
删除锁的操作
版权声明:本文为博主原创文章,未经博主允许不得转载。
文章内容 0.序 1.Nginx锁的核心数据结构 2.分析文件锁 |
强烈推荐的文章是nginx中锁的设计以及惊群的处理,本文只是对文章中的内容进行了部分总结和部分解释。
Nginx之所以要采用Accept互斥锁就是为了避免惊群现象。
所谓惊群现象:指一个fd的事件被触发后,等候这个fd的所有线程/进程都被唤醒。虽然都被唤醒,但是只有一个会去响应。
Nginx使用的锁分为两种情况:1)一种是支持原子操作的情况,由NGX_H***E_ATOMIC_OPS这个宏来进行控制的。2)一种是不支持原子操作的情况,使用文件锁来实现。
用户空间进程间锁的实现的原理很简单,就是弄出一个让所有进程共享的东西,比如mmap的内容或文件,然后通过这个东西来控制进程的互斥。
1.Nginx锁的核心数据结构
Nginx中锁的核心数据结构,也就是Nginx使用哪个变量来控制进程的行为。
typedef struct { #if (NGX_H***E_ATOMIC_OPS) ngx_atomic_t *lock; //原子操作 #else ngx_fd_t fd; //fd表示进程间共享的文件句柄 u_char *name; //name表示文件名 #endif } ngx_shmtx_t; |
由于本人对原子操作不了解,因此只能分析一下文件锁。Nginx使用文件锁,实际上就是APUE 14.3 记录锁中内容。
上锁:ngx_trylock_accept_mutex----------------->ngx_shmtx_trylock--------------->ngx_trylock_fd
删除锁:ngx_shmtx_unlock------------------------->ngx_unlock_fd
ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle) { /*ngx_shmtx_trylock通过fcntl对ngx_accept_mutex中fd成员变量上锁,锁定整个文件*/ if (ngx_shmtx_trylock(&ngx_accept_mutex)) {/*锁定成功*/ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "accept mutex locked"); /*ngx_accept_mutex_held 获得锁的标志,如果本来已经获得锁,则直接返回OK*/ if (ngx_accept_mutex_held && ngx_accept_events == 0 && !(ngx_event_flags & NGX_USE_RTSIG_EVENT)) { return NGX_OK; } /*ngx_enable_accept_events将cycle->listening中的端口号都加到epoll事件中。把进程对应的监听socket 放入到epoll中进行监听,这样只有该进程能监听到accept操作。*/ if (ngx_enable_accept_events(cycle) == NGX_ERROR) { ngx_shmtx_unlock(&ngx_accept_mutex); return NGX_ERROR; } ngx_accept_events = 0; ngx_accept_mutex_held = 1;/*获得锁的标识*/ return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "accept mutex lock failed: %ui", ngx_accept_mutex_held); /*//如果我们前面已经获得了锁,然后这次获得锁失败,则说明当前的listen句柄已经被其他的进程锁监听,因此此时需要从epoll中移出调已经注册的listen句柄。这样就很好的控制了子进程的负载均衡 */ if (ngx_accept_mutex_held) { /*ngx_disable_accept_events将cycle->listening中的端口号从epoll事件中删除*/ if (ngx_disable_accept_events(cycle) == NGX_ERROR) { return NGX_ERROR; } ngx_accept_mutex_held = 0; } return NGX_OK; } |
ngx_shmtx_trylock(ngx_shmtx_t *mtx){ ngx_err_t err; err = ngx_trylock_fd(mtx->fd); if (err == 0) { return 1; } } |
ngx_err_t ngx_trylock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0; } |
void ngx_shmtx_unlock(ngx_shmtx_t *mtx) { ngx_err_t err; err = ngx_unlock_fd(mtx->fd); if (err == 0) { return; } ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); } |
ngx_err_t ngx_unlock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0; } |
相关文章推荐
- 文章6:Nginx中的Epoll事件处理机制
- 文章8:Nginx中与Master-Worker进程通信有关的内容
- 文章5:Nginx源码分析--事件循环
- 文章4:Nginx进程管理之 ngx_master_process_cycle分析
- 文章2:nginx启动过程之main函数
- 文章3:Nginx中与信号有关的内容
- 文章0:Nginx整体分析
- 文章1:Nginx启动过程分析
- 在ubuntu中用apt-get安装LEMP栈(linux+nginx+mysql+php)
- 《深入理解Nginx 模块开发与架构解析》笔记之epoll事件模块
- Nginx配置文件详细说明
- Nginx安装
- 绝对详细!Nginx基本配置、性能优化指南
- nginx的keep-alive配置
- Nginx学习之十一-Nginx启动框架处理流程
- Nginx学习之十二-负载均衡-加权轮询策略剖析
- nginx源码分析—启动流程
- nginx源码分析—全局变量ngx_cycle的初始化
- Nginx如何解决“惊群”现象
- win7配置nginx+php步骤