Nginx 학습 의 7 - 모듈 ngxepoll_module 상세 설명 (epoll 메커니즘 이 nginx 에서 의 실현)

16881 단어 linuxnginx서버epoll
본 고 는 실제 코드 와 결합 하여 사건 구동 모듈 인터페이스 와 epoll 용법 이 어떻게 결합 하여 역할 을 발휘 하 는 지 소개 한다.Nginx 중 ngxepoll_module 모듈 은 epoll 을 기반 으로 Nginx 의 이벤트 구동 을 실현 합 니 다.
모듈 정의 (src / event / ngx epoll module. c):
ngx_module_t  ngx_epoll_module = {
    NGX_MODULE_V1,
    &ngx_epoll_module_ctx,               /* module context */
    ngx_epoll_commands,                  /* module directives */
    NGX_EVENT_MODULE,                    /* module type */
    NULL,                                /* init master */
    NULL,                                /* init module */
    NULL,                                /* init process */
    NULL,                                /* init thread */
    NULL,                                /* exit thread */
    NULL,                                /* exit process */
    NULL,                                /* exit master */
    NGX_MODULE_V1_PADDING
};

우선 ngxepoll_module 모듈 은 어떤 설정 항목 에 관심 이 있 습 니까? ngxepoll_commands 배열 정 의 는 다음 과 같 습 니 다.
static ngx_command_t  ngx_epoll_commands[] = {
    //           epoll_wait          
    { ngx_string("epoll_events"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_num_slot,//            
      0,
      offsetof(ngx_epoll_conf_t, events),
      NULL },

    //       I/O   io_setup         I/O      ,       I/O    
    { ngx_string("worker_aio_requests"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_num_slot,
      0,
      offsetof(ngx_epoll_conf_t, aio_requests),
      NULL },

      ngx_null_command
};

설정 항목 을 저장 하 는 구조 체 ngxepoll_conf_t:
ngx_event_module_t  ngx_epoll_module_ctx = {
    &epoll_name,
    ngx_epoll_create_conf,               /* create configuration */
    ngx_epoll_init_conf,                 /* init configuration */

    {
        ngx_epoll_add_event,             /* add an event */
        ngx_epoll_del_event,             /* delete an event */
        ngx_epoll_add_event,             /* enable an event */
        ngx_epoll_del_event,             /* disable an event */
        ngx_epoll_add_connection,        /* add an connection */
        ngx_epoll_del_connection,        /* delete an connection */
        NULL,                            /* process the changes */
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};

유형 ngxevent_module_t(src/event/ngx_event.h):
typedef struct {
    ngx_str_t              *name;//      

    void                 *(*create_conf)(ngx_cycle_t *cycle);//      ,                     
    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);//        ,                            

    ngx_event_actions_t     actions;
} ngx_event_module_t;

그 중에서 actions 는 사건 구동 체제 에 대응 하여 모든 사건 모듈 이 실현 해 야 할 10 가지 추상 적 인 방법 이다.
typedef struct {
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);//    
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);//    

    //         ,               
    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*add_conn)(ngx_connection_t *c);//    ,                         
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

    //          ,  Nginx   
    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    //         ,                。        ngx_process_events_and_timers   ,    、       
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                   ngx_uint_t flags);

    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);//         
    void       (*done)(ngx_cycle_t *cycle);//              
} ngx_event_actions_t;

다음은 몇 가지 중요 한 방법 을 보 겠 습 니 다.
init 인 터 페 이 스 를 실현 하 는 방법 ngxepoll_init, 그것 은 두 가지 일 을 했다.
(1) epoll 호출create 방법 epoll 대상 만 들 기
(2) 이벤트 생 성list 배열, epoll 진행 에 사용wait 호출 시 커 널 대상 전달
원본 코드:
static int                  ep = -1;//epoll     
static struct epoll_event  *event_list;//  
static ngx_uint_t           nevents;//         

static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
    ngx_epoll_conf_t  *epcf;//         

    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);//            

    if (ep == -1) {
        //  epoll  ,   Linux     ,           
        ep = epoll_create(cycle->connection_n / 2);

        //    
        if (ep == -1) {
            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                          "epoll_create() failed");
            return NGX_ERROR;
        }

#if (NGX_HAVE_FILE_AIO)
        //  I/O
        ngx_epoll_aio_init(cycle, epcf);

#endif
    }

    //        epoll_wait              
    if (nevents < epcf->events) {
        if (event_list) {
            //              
            ngx_free(event_list);
        }

        //        
        event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
                               cycle->log);
        if (event_list == NULL) {
            return NGX_ERROR;
        }
    }

    //    
    nevents = epcf->events;

    ngx_io = ngx_os_io;

    //Nginx              
    ngx_event_actions = ngx_epoll_module_ctx.actions;

#if (NGX_HAVE_CLEAR_EVENT)
    //    E    epoll,NGX_HAVE_CLEAR_EVENT         nginx  ET  
    ngx_event_flags = NGX_USE_CLEAR_EVENT
#else
    ngx_event_flags = NGX_USE_LEVEL_EVENT
#endif
                      |NGX_USE_GREEDY_EVENT
                      |NGX_USE_EPOLL_EVENT;

    return NGX_OK;
}

다음은 ngxepoll_add_이벤트 방법 을 예 로 들 면 그들 이 epoll 를 어떻게 호출 하 는 지 봅 시다.ctl 에서 epoll 에 이 벤트 를 추가 합 니 다.
static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
    int                  op;
    uint32_t             events, prev;
    ngx_event_t         *e;
    ngx_connection_t    *c;
    struct epoll_event   ee;

    //     data         ngx_connection_t  
    c = ev->data;

    //     event                 ,    events   EPOLLIN    EPOLLOUT   
    events = (uint32_t) event;

    //           :         active                 
    /*  ,
    man epoll:
    Q:  What  happens  if you register the same file descriptor on an epoll  instance twice?
 
    A: You will probably get EEXIST.  However, it is  possible  to  add  a   duplicate  (dup(2),  dup2(2),  fcntl(2)  F_DUPFD) descriptor to the  same epoll instance.  This can be a useful technique for  filtering   events,  if the duplicate file descriptors are registered with different events masks.*/

    /*  nginx            ,   epoll      fd   ( NGX_READ_EVENT)    ,nginx       fd         , e=c->write,    e->active 1,   fd     NGX_WRITE_EVENT     epoll  ,       mod  ,         ,     add  ,  fd   epoll 。    NGX_WRITE_EVENT       。*/
    
    if (event == NGX_READ_EVENT) {
        e = c->write;
        prev = EPOLLOUT;
#if (NGX_READ_EVENT != EPOLLIN)
        events = EPOLLIN;
#endif

    } else {
        e = c->read;
        prev = EPOLLIN;
#if (NGX_WRITE_EVENT != EPOLLOUT)
        events = EPOLLOUT;
#endif
    }

    //  active            
    if (e->active) {
        op = EPOLL_CTL_MOD;//     ,    
        events |= prev;

    } else {
        op = EPOLL_CTL_ADD;//      ,    
    }

    ee.events = events | (uint32_t) flags;//  events   
    //ptr    ngx_connection_t  ,instance        
    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                   "epoll add event: fd:%d op:%d ev:%08XD",
                   c->fd, op, ee.events);

    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                      "epoll_ctl(%d, %d) failed", op, c->fd);
        return NGX_ERROR;
    }

    ev->active = 1;
#if 0
    ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
#endif

    return NGX_OK;
}

ngx_epoll_process_이 벤트 는 이 벤트 를 수집, 배포 하 는 프로 세 스 를 실현 합 니 다.이벤트 인터페이스의 방법, 코드 는 다음 과 같 습 니 다.
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
    int                events;
    uint32_t           revents;
    ngx_int_t          instance, i;
    ngx_uint_t         level;
    ngx_err_t          err;
    ngx_event_t       *rev, *wev, **queue;
    ngx_connection_t  *c;

    /* NGX_TIMER_INFINITE == INFTIM */

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "epoll timer: %M", timer);

    //  epoll_wait    
    events = epoll_wait(ep, event_list, (int) nevents, timer);

    err = (events == -1) ? ngx_errno : 0;

    //    
    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
        ngx_time_update();
    }

    //epoll_wait    
    if (err) {
        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {
                ngx_event_timer_alarm = 0;
                return NGX_OK;
            }

            level = NGX_LOG_INFO;

        } else {
            level = NGX_LOG_ALERT;
        }

        ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
        return NGX_ERROR;
    }

    //          
    if (events == 0) {
        if (timer != NGX_TIMER_INFINITE) {
            return NGX_OK;
        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                      "epoll_wait() returned no events without timeout");
        return NGX_ERROR;
    }

    ngx_mutex_lock(ngx_posted_events_mutex);

    //    epoll_wait       
    for (i = 0; i < events; i++) {
        //    ngx_connection_t   
        c = event_list[i].data.ptr;

        //               :    instance  ,     
        instance = (uintptr_t) c & 1;
        //   32   64   ,          0,      
        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
        //     
        rev = c->read;

        //            
        if (c->fd == -1 || rev->instance != instance) {

            /*
             * the stale event from a file descriptor
             * that was just closed in this iteration
             */

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll: stale event %p", c);
            continue;
        }

        //      
        revents = event_list[i].events;

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "epoll: fd:%d ev:%04XD d:%p",
                       c->fd, revents, event_list[i].data.ptr);

        if (revents & (EPOLLERR|EPOLLHUP)) {
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll_wait() error on fd:%d ev:%04XD",
                           c->fd, revents);
        }

#if 0
        if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "strange epoll_wait() events fd:%d ev:%04XD",
                          c->fd, revents);
        }
#endif

        if ((revents & (EPOLLERR|EPOLLHUP))
             && (revents & (EPOLLIN|EPOLLOUT)) == 0)
        {
            /*
             * if the error events were returned without EPOLLIN or EPOLLOUT,
             * then add these flags to handle the events at least in one
             * active handler
             */

            revents |= EPOLLIN|EPOLLOUT;
        }

        //            
        if ((revents & EPOLLIN) && rev->active) {

            if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
                rev->posted_ready = 1;

            } else {
                rev->ready = 1;
            }

            //        
            if (flags & NGX_POST_EVENTS) {
                /*    post          ,                  
                         ngx_posted_accept_events    ngx_posted_events   。*/
                queue = (ngx_event_t **) (rev->accept ?
                               &ngx_posted_accept_events : &ngx_posted_events);
                //               
                ngx_locked_post_event(rev, queue);

            } else {
                //                 
                rev->handler(rev);
            }
        }

        wev = c->write;

        if ((revents & EPOLLOUT) && wev->active) {

            if (c->fd == -1 || wev->instance != instance) {

                /*
                 * the stale event from a file descriptor
                 * that was just closed in this iteration
                 */

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "epoll: stale event %p", c);
                continue;
            }

            if (flags & NGX_POST_THREAD_EVENTS) {
                wev->posted_ready = 1;

            } else {
                wev->ready = 1;
            }

            if (flags & NGX_POST_EVENTS) {
                ngx_locked_post_event(wev, &ngx_posted_events);

            } else {
                wev->handler(wev);
            }
        }
    }

    ngx_mutex_unlock(ngx_posted_events_mutex);

    return NGX_OK;
}

위 코드 에 나타 난 만 료 사건 을 설명 하 겠 습 니 다.
예 를 들 어 epollwait 는 한 번 에 세 개의 사건 을 되 돌려 줍 니 다. 첫 번 째 사건 을 처리 하 는 과정 에서 업무 의 필요 로 연결 을 닫 았 습 니 다. 이 연결 은 세 번 째 사건 에 적합 합 니 다.이렇게 해서 세 번 째 사건 을 처리 할 때 이 사건 은 기한 이 지난 사건 이 고 처리 하면 실 수 를 할 수 있다.
그렇다면 이 문 제 는 어떻게 해결 할 것 인가?닫 힌 이 연결 의 fd 소켓 을 - 1 로 설정 합 니까?이렇게 하면 결코 모든 문 제 를 해결 할 수 없다.원인 은 ngxconnection_t 의 복용.
세 번 째 사건 에 대응 하 는 ngx 를 가정 합 니 다.connection_t 연결 중의 fd 소켓 은 원래 10 이 었 습 니 다. 첫 번 째 사건 을 처리 할 때 이 연결 의 소켓 을 닫 았 습 니 다. 동시에 - 1 로 설정 하고 ngx 를 호출 합 니 다.free_connection 은 이 연결 을 연결 풀 에 돌려 줍 니 다.재 ngxprocess_이벤트 방법의 순환 에서 i 가 두 번 째 사건 을 처리 하기 시 작 했 습 니 다. 마침 두 번 째 사건 은 새로운 연결 사건 을 만 들 고 ngx 를 호출 하 는 것 입 니 다.get_connection 이 연결 풀 에서 꺼 낸 연결 은 방금 풀 린 세 번 째 이벤트 에 대응 하 는 연결 일 수 있 습 니 다.소켓 10 이 방금 풀 려 났 기 때문에 Liux 커 널 은 방금 풀 린 소켓 10 을 새로 만 든 연결 에 배분 할 가능성 이 매우 높다.따라서 세 번 째 사건 을 순환 적 으로 처리 할 때 이 시간 은 만 료 된다!새로 만 든 연결 이 아니 라 닫 힌 연결 입 니 다.
어떻게 이 문 제 를 해결 합 니까?인 스 턴 스 로 위 치 를 표시 합 니 다.호출 시 ngxget_connection 이 연결 풀 에서 새로운 연결 을 가 져 올 때 인 스 턴 스 플래그 위치 가 거꾸로 설 치 됩 니 다.다음은 함수 ngxget_connection 에 대응 하 는 코드 (src / core / ngx connection. c):
ngx_connection_t *
ngx_get_connection(ngx_socket_t s, ngx_log_t *log)
{
    ngx_uint_t         instance;
    ngx_event_t       *rev, *wev;
    ngx_connection_t  *c;

    /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */

    if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) {
        ngx_log_error(NGX_LOG_ALERT, log, 0,
                      "the new socket has number %d, "
                      "but only %ui files are available",
                      s, ngx_cycle->files_n);
        return NULL;
    }

    /* ngx_mutex_lock */

    //           
    c = ngx_cycle->free_connections;

    if (c == NULL) {
        ngx_drain_connections();
        c = ngx_cycle->free_connections;
    }

    if (c == NULL) {
        ngx_log_error(NGX_LOG_ALERT, log, 0,
                      "%ui worker_connections are not enough",
                      ngx_cycle->connection_n);

        /* ngx_mutex_unlock */

        return NULL;
    }

    ngx_cycle->free_connections = c->data;
    ngx_cycle->free_connection_n--;

    /* ngx_mutex_unlock */

    if (ngx_cycle->files) {
        ngx_cycle->files[s] = c;
    }

    //         
    rev = c->read;
    wev = c->write;

    ngx_memzero(c, sizeof(ngx_connection_t));

    c->read = rev;
    c->write = wev;
    c->fd = s;
    c->log = log;

    //  instance   
    instance = rev->instance;

    ngx_memzero(rev, sizeof(ngx_event_t));
    ngx_memzero(wev, sizeof(ngx_event_t));

    //instance     
    rev->instance = !instance;
    wev->instance = !instance;

    rev->index = NGX_INVALID_INDEX;
    wev->index = NGX_INVALID_INDEX;

    rev->data = c;
    wev->data = c;

    wev->write = 1;

    return c;
}

이렇게 하면 어떠한 비용 도 증가 하지 않 고 서버 개발 시 발생 하 는 기한 이 지난 사건 문 제 를 해결 할 수 있다.
done 인 터 페 이 스 를 실현 하 는 ngxepoll_done 방법 은 nginx 가 서 비 스 를 종료 할 때 호출 됩 니 다.주로 완 료 된 작업 은 epoll 설명자 ep 를 닫 고 이벤트 를 방출 하 는 것 입 니 다.list 배열.
참고 자료:
CSDN 블 로그: ngxepoll_add_이벤트 설명
& lt; Nginx 깊이 이해 & gt;
http://www.alidata.org/archives/1296

좋은 웹페이지 즐겨찾기