关于ngx_trylock_accept_mutex的一些解释

关于nginx里面accept互斥锁的处理,群里讨论了很多次,很多人都提出了各种问题,比如问到:在ngx_process_events_and_timers中,为什么在释放ngx_accept_mutex之后,不把ngx_accept_mutex_held清零?
if (ngx_accept_mutex_held) {
    ngx_shmtx_unlock(&ngx_accept_mutex);
    /* 有人说应该加上ngx_accept_mutex_held = 0; */
}
这里我们好好分析一下ngx_trylock_accept_mutex函数,它就能给我们答案:
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 1;

        return NGX_OK;
    }

    if (ngx_accept_mutex_held) {
        if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 0;
    }

    return NGX_OK;
}
对照这个函数,我们假想一个情景,若此时nginx有两个worker,我们称为A和B。当A执行这个函数,并在调用ngx_shmtx_trylock时成功,这时它将listen fd注册到自己的epoll中(即ngx_enable_accept_events),然后ngx_accept_mutex_held被置1。注意哦,这里ngx_enable_accept_events处理之后就将其置1是为了表达listen fd此时被A进程注册到epoll中了。很显然这个时候B进程由于获取accept锁失败,自然就没有权利accept了。当进程A在处理完accept之后,就会释放accept锁,让B在下一轮竞争中能有机会获取锁来做accept。
if (ngx_accept_mutex_held) {
    ngx_shmtx_unlock(&ngx_accept_mutex);
}
接下来B很争气,获得了accept锁,跟当年进程A一样将listen fd加到epoll中,处理过程如出一辙。这个时候我们来看A进程,由于这次在跟B的较量中败北,但是ngx_accept_mutex_held为true,表明之前曾经注册过listen fd。别忘了失败的代价就是要交出listen fd,皇帝的位置现在由B来做,王冠现在不属于你了。由于此时只有B有权将listen fd注册到自己的epoll中,其他的进程(ngx_accept_mutex_held为true的进程)就要将listen fd从自己的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 = 0;
}
说到这里大家别混淆了,nginx的epoll是每个进程私有了,可能在有些系统的设计里,epoll是线程(或者进程)共享的。
 

你可能感兴趣的:(关于ngx_trylock_accept_mutex的一些解释)