文章9:Nginx accept互斥锁

来源:互联网 发布:js 数字转换为汉字 编辑:程序博客网 时间:2024/06/05 04:30
欢迎转载,转载请注明出处http://blog.csdn.net/yankai0219/article/details/8453317
文章内容

0.序
1.Nginx锁的核心数据结构
2.分析文件锁


0.序
强烈推荐的文章是nginx中锁的设计以及惊群的处理,本文只是对文章中的内容进行了部分总结和部分解释。
Nginx之所以要采用Accept互斥锁就是为了避免惊群现象。
所谓惊群现象:指一个fd的事件被触发后,等候这个fd的所有线程/进程都被唤醒。虽然都被唤醒,但是只有一个会去响应。

Nginx使用的锁分为两种情况:1)一种是支持原子操作的情况,由NGX_HAVE_ATOMIC_OPS这个宏来进行控制的。2)一种是不支持原子操作的情况,使用文件锁来实现。
用户空间进程间锁的实现的原理很简单,就是弄出一个让所有进程共享的东西,比如mmap的内容或文件,然后通过这个东西来控制进程的互斥。
1.Nginx锁的核心数据结构
Nginx中锁的核心数据结构,也就是Nginx使用哪个变量来控制进程的行为。

  1. typedef struct {  
  2. #if (NGX_HAVE_ATOMIC_OPS)  
  3.     ngx_atomic_t  *lock;  //原子操作
  4. #else  
  5.     ngx_fd_t       fd;  //fd表示进程间共享的文件句柄
  6.     u_char        *name;  //name表示文件名
  7. #endif  
  8. } ngx_shmtx_t;  



2.分析文件锁
     由于本人对原子操作不了解,因此只能分析一下文件锁。Nginx使用文件锁,实际上就是APUE 14.3 记录锁中内容。
上锁:ngx_trylock_accept_mutex----------------->ngx_shmtx_trylock--------------->ngx_trylock_fd
删除锁:ngx_shmtx_unlock------------------------->ngx_unlock_fdngx_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;
}



原创粉丝点击