nginx事件(ngx_event_actions_t)

事件模块启动流程:
  • 在ngx_init_cycle() 中初始化完配置信息后
  • work进程的启动调用方法ngx_worker_process_cycle()
  • 在ngx_worker_process_init() 中调用模块的 init_process() 启动各模块(源码中只有ngx_event_core_module模块的ngx_event_process_init(),其它的模块为NULL)
    • ngx_event_core_module->ngx_event_process_init()
    • 初始化nginx的事件列表ngx_posted_accept_events和ngx_posted_events
      • ngx_queue_init(&ngx_posted_accept_events);
      • ngx_queue_init(&ngx_posted_events);
    • 启动事件引擎(select、poll、epoll),调用NGX_EVENT_MODULE类型模块的module->ctx>actions->init()
      • ngx_devpoll_module模块的 ngx_devpoll_module->ctx>actions->ngx_devpoll_init()
      • ngx_epoll_module模块的 ngx_epoll_module->ctx>actions->ngx_epoll_init()
      • ngx_eventport_module模块的 ngx_eventport_module->ctx>actions->ngx_eventport_init()
      • ngx_iocp_module模块的 ngx_iocp_module->ctx>actions->ngx_iocp_init()
      • ngx_select_module模块的 ngx_select_module->ctx>actions->ngx_select_init()
      • win32的ngx_select_module模块的 ngx_select_module->ctx>actions->ngx_select_init()
    • 初始cycle的读写事件数组
    • 初始化连接池
    • 遍历listen列表来,绑定空闲连接,并设置初始化tcp/UNIX域的accep类型I/O的read->handlerr=ngx_event_accept或ngx_event_recvmsg
    • 由于拿不到ngx_use_accept_mutex,下面的事件添加放到了:ngx_process_events_and_timers
  • 初始化完后,work进程循环调用nginx的事件处理器ngx_process_events_and_timers()
      1. 获取ngx_use_accept_mutexngx_trylock_accept_mutex()->ngx_enable_accept_events()->ngx_add_event(),注册accept到I/O事件引擎上
        • 这里调用各I/O事件引擎的addha函数如下
        • ngx_devpoll_module模块的 ngx_devpoll_module->ctx>actions->ngx_devpoll_add_event()
        • ngx_epoll_module模块的 ngx_epoll_module->ctx>actions->ngx_epoll_add_event()
        • ngx_eventport_module模块的 ngx_eventport_module->ctx>actions->ngx_eventport_add_event()
        • ngx_iocp_module模块的 ngx_iocp_module->ctx>actions->ngx_iocp_add_event()
        • ngx_select_module模块的 ngx_select_module->ctx>actions->ngx_select_add_event()
        • win32的ngx_select_module模块的 ngx_select_module->ctx>actions->ngx_select_add_event()
      1. ngx_process_events回调,用来处理I/O引擎的时间,即 ngx_epoll_module->ctx>actions->ngx_epoll_process_events()
        • I/O引擎完成套接字注册事件是完成的是读、写事件,则封装成nginx的read、write事件,放在nginx的全局ngx_posted_events事件池中。
        • 如果read不是NGX_READ_EVENT(即accept类的读I/O事件)则直接调用,ngx_posted_accept_events和ngx_posted_events
      1. ngx_event_process_posted:nginx的事件处理函数,处理ngx_posted_accept_events和ngx_posted_events 事件队列(http、mai、stream调用后设置read、write的事件处理函数)
        • ngx_event_process_posted(cycle, &ngx_posted_accept_events);
          • 执行ngx_event_accept()或ngx_event_recvmsg()
            • http模块配置设置时的listen->heandler = ngx_http_init_connection
            • mail模块配置设置时的listen->heandler = ngx_mail_init_connection
            • stream模块配置设置时的listen->heandler = ngx_stream_init_connection
          • ngx_event_recvmsg()直接接受UNIX进程消息
        • ngx_event_process_posted(cycle, &ngx_posted_events),将I/O事件调加到nginx事件列表中;
结构:ngx_event_s
  • 在nginx中运行的都是event,而epoll中运行的都是套接字。所有直接将套接字和event关联起来
struct ngx_event_s {
    
    /* 事件上下文数据,通常data都是指向ngx_connection_t连接对象。
     * 开启文件异步I/O时,它可能会指向ngx_event_aio_t结构体。
     */
    void            *data;  

    /* 标志位,为1时表示事件是可写的。通常它表示对应的TCP连接可写,也就是连接处于可以发送网络包的状态。*/
    unsigned         write:1;   
    
    /* 标志位,为1时表示为此事件可以建立新的连接。
        通常在ngx_cycle_t中的listening动态数组中,每一个监听对象ngx_listening_t,对应的读事件中的accept标志位才会是1。
    */
    unsigned         accept:1;  

    /* used to detect the stale events in kqueue, rtsig, and epoll
     * 这个标志位用于区分当前事件是否过期,它仅仅是给事件驱动模块使用的,而事件消费模块可不用关心。
     * 为什么需要这个标志位呢?当开始处理一批事件时,处理前面的事件可能会关闭一些连接,
       而这些连接有可能影响这批事件中还未处理到的后面的事件,这时可通过instance来避免处理后面的过期事件。
     */
    unsigned         instance:1;

    /* the event was passed or would be passed to a kernel;
     * in aio mode - operation was posted.
     * 标志位,为1表示当前事件是活跃的,为0表示事件是不活跃的。
     * 这个状态对应着事件驱动模块处理方式的不同。例如,在添加事件,删除事件和处理事件时,
     * active标志位的不同都会对应着不同的处理方式。在使用事件时,一般不会直接改变active标志位。
     */
    unsigned         active:1;
    
     /* 标志位,为1表示禁用事件,仅在kqueue或者rtsig事件驱动模块中有效,对于epoll事件驱动模块则没有意义。
     */
    unsigned         disabled:1;

    /* the ready event; in aio mode 0 means that no operation can be posted 
     * 标志位,为1表示当前事件准备就绪,也就是说,允许这个事件的handler处理这个事件。
     * 在HTTP框架中,经常会检查事件的ready标志位,以确定是否可以接收请求或者发送相应。
     */ 
    unsigned         ready:1;

    /* 该标志位仅对kqueue,eventport等模块有意义,而对于linux上的epoll事件驱动模块则是无意义的。*/
    unsigned         oneshot:1;

    /* aio operation is complete 用于异步aio事件的处理*/
    unsigned         complete:1;
    
    /* 标志位,eof表示当前处理的字符流已经结束,error表示事件处理过程出错了*/
    unsigned         eof:1;
    unsigned         error:1;

    /* 标志位,为1表示这个事件超时,用以提示handler做超时处理,它与timer_set都用了定时器 */
    unsigned         timedout:1;
    unsigned         timer_set:1;
    
     /* 标志位,delayed为1表示需要延迟处理这个事件,它仅用于限速功能 */
    unsigned         delayed:1;

    /* 标志位,为1表示延迟建立TCP连接,也就是TCP三次握手后并不建立连接,而是等到真正收到数据包后才建连接 */
    unsigned         deferred_accept:1;

    /* the pending eof reported by kqueue, epoll or in aio chain operation */
    /* 标志位,为1表示等待字符流结束,它只与kqueue和aio事件驱动机制有关 */
    unsigned         pending_eof:1;
    //接受、读、写
    unsigned         posted:1;
    //关闭
    unsigned         closed:1;

    /* to test on worker exit */
    unsigned         channel:1;
    unsigned         resolver:1;

    unsigned         cancelable:1;

#if (NGX_HAVE_KQUEUE)
    unsigned         kq_vnode:1;

    /* the pending errno reported by kqueue */
    int              kq_errno;
#endif

    /*
     * kqueue only:
     *   accept:     number of sockets that wait to be accepted
     *   read:       bytes to read when event is ready
     *               or lowat when event is set with NGX_LOWAT_EVENT flag
     *   write:      available space in buffer when event is ready
     *               or lowat when event is set with NGX_LOWAT_EVENT flag
     *
     * epoll with EPOLLRDHUP:
     *   accept:     1 if accept many, 0 otherwise
     *   read:       1 if there can be data to read, 0 otherwise
     *
     * iocp: TODO
     *
     * otherwise:
     *   accept:     1 if accept many, 0 otherwise
     */

#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)
    int              available;
#else
    unsigned         available:1;
#endif
    
    /* 这个事件发生时的处理方法 */
    ngx_event_handler_pt  handler;


#if (NGX_HAVE_IOCP)
    ngx_event_ovlp_t ovlp;
#endif

    ngx_uint_t       index;

    ngx_log_t       *log;

    ngx_rbtree_node_t   timer;

    /* the posted queue */
    ngx_queue_t      queue;

#if 0

    /* the threads support */

    /*
     * the event thread context, we store it here
     * if $(CC) does not understand __thread declaration
     * and pthread_getspecific() is too costly
     */

    void            *thr_ctx;

#if (NGX_EVENT_T_PADDING)

    /* event should not cross cache line in SMP */

    uint32_t         padding[NGX_EVENT_T_PADDING];
#endif
#endif
};
结构:ngx_event_aio_s
struct ngx_event_aio_s {
    void                      *data;
    ngx_event_handler_pt       handler;
    ngx_file_t                *file;

    ngx_fd_t                   fd;

#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
    ssize_t                  (*preload_handler)(ngx_buf_t *file);
#endif

#if (NGX_HAVE_EVENTFD)
    int64_t                    res;
#endif

#if !(NGX_HAVE_EVENTFD) || (NGX_TEST_BUILD_EPOLL)
    ngx_err_t                  err;
    size_t                     nbytes;
#endif

    ngx_aiocb_t                aiocb;
    ngx_event_t                event;
};
结构:ngx_event_actions_t:nginx的epoll模块处理函数封装结构。
  • 主要用来将 epoll事件处理成nginx事件,nginx事件处理成 epoll 事件
typedef struct {
    /* 添加/移出事件方法,负责把事件添加/移出到操作系统提供的事件驱动机制(如epoll,kqueue等)中,
      这样在事件发生之后,将可以/无法调用下面的process_envets时获取这个事件。
    */
    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);

    /* 启用/禁用一个事件,目前事件框架不会调用,大部分事件驱动模块对该方法的实现都与add/del完全一致 *
    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);

    ngx_int_t  (*notify)(ngx_event_handler_pt handler);
    
    'nginx的核心事件,将epoll事件封装到nginx事件列表中'
    '启动nginx的事件列表'
    /* 在正常的工作循环中,将通过调用process_events方法来处理事件。
     * 这个方法仅在ngx_process_events_and_timers方法中调用,它是处理分发事件的核心。*/
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                                 ngx_uint_t flags);

    /* 初始化和退出事件驱动模块的方法:初始化epoll */
    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;

事件进程启动命令:ngx_event_process_init:
  • 初始化所有的connection结构体链表,放在cycle->freee_connections 中
  • 从cycle->freee_connections获取空闲连接,绑定listen数组中的套接字
  • 根据listen->type来,初始化tcp/UNIX域的accep类型I/O的read->handler =ngx_event_accept?ngx_event_recvmsg
  • 由于拿不到ngx_use_accept_mutex,下面的事件添加放到了:ngx_process_events_and_timers中
    • 通过ngx_add_event()将这个连接,以accept的类型注册放到选择的I/O事件引擎上**(epoll、select、poll)**
/**
    模块的进程启动函数
*/
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
    ngx_uint_t           m, i;
    ngx_event_t         *rev, *wev;
    ngx_listening_t     *ls;
    ngx_connection_t    *c, *next, *old;
    ngx_core_conf_t     *ccf;
    ngx_event_conf_t    *ecf;
    ngx_event_module_t  *module;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
        ngx_use_accept_mutex = 1;
        ngx_accept_mutex_held = 0;
        ngx_accept_mutex_delay = ecf->accept_mutex_delay;

    } else {
        ngx_use_accept_mutex = 0;
    }

    //初始化nginx的事件列表
    ngx_queue_init(&ngx_posted_accept_events);
    ngx_queue_init(&ngx_posted_events);

    if (ngx_event_timer_init(cycle->log)  NGX_ERROR) {
        return NGX_ERROR;
    }
    //初始化 NGX_EVENT_MODULE类型模块的事件上下文:
    // epoll事件引擎:epoll_create
    for (m = 0; cycle->modules[m]; m++) {
        if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }

        if (cycle->modules[m]->ctx_index != ecf->use) {
            continue;
        }

        module = cycle->modules[m]->ctx;

        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
            /* fatal */
            exit(2);
        }

        break;
    }


    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                      "the \"timer_resolution\" directive is not supported "
                      "with the configured event method, ignored");
        ngx_timer_resolution = 0;
    }


    cycle->connections =
        ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
    if (cycle->connections  NULL) {
        return NGX_ERROR;
    }

    c = cycle->connections;

    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                   cycle->log);
    if (cycle->read_events  NULL) {
        return NGX_ERROR;
    }

    rev = cycle->read_events;
    for (i = 0; i < cycle->connection_n; i++) {
        rev[i].closed = 1;
        rev[i].instance = 1;
    }

    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                    cycle->log);
    if (cycle->write_events  NULL) {
        return NGX_ERROR;
    }

    wev = cycle->write_events;
    for (i = 0; i < cycle->connection_n; i++) {
        wev[i].closed = 1;
    }

    i = cycle->connection_n;
    next = NULL;

    do {
        i--;

        c[i].data = next;
        c[i].read = &cycle->read_events[i];
        c[i].write = &cycle->write_events[i];
        c[i].fd = (ngx_socket_t) -1;

        next = &c[i];
    } while (i);

    cycle->free_connections = next;
    cycle->free_connection_n = cycle->connection_n;

    /* for each listening socket */
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {


        c = ngx_get_connection(ls[i].fd, cycle->log);

        if (c  NULL) {
            return NGX_ERROR;
        }

        c->type = ls[i].type;
        c->log = &ls[i].log;

        c->listening = &ls[i];
        ls[i].connection = c;

        rev = c->read;

        rev->log = c->log;
        rev->accept = 1;
        if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
            if (ls[i].previous) {
                old = ls[i].previous->connection;
                if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
                     NGX_ERROR)
                {
                    return NGX_ERROR;
                }

                old->fd = (ngx_socket_t) -1;
            }
        }
        '初始化tcp/UNIX域的accept事件'
        rev->handler = (c->type  SOCK_STREAM) ? ngx_event_accept
                                                : ngx_event_recvmsg;
        '获取ngx_use_accept_mutex'
        if (ngx_use_accept_mutex) { 
            continue;
        }
        '获取ngx_use_accept_mutex成功后,将该套接字注册到I/O引擎上'
        if (ngx_add_event(rev, NGX_READ_EVENT, 0)  NGX_ERROR) {
            return NGX_ERROR;
        }
    return NGX_OK;
}
work的事件处理函数(nginx的事件处理模块):ngx_process_events_and_timers
  • ngx_trylock_accept_mutex 当获取到标志位后才进行** accept 事件注册**

  • ngx_process_events 处理事件

  • 释放 accept_mutex 锁

  • ngx_event_process_posted 处理 posted 队列的事件

  • ngx_process_events_and_timers()

/**
    nginx的事件初始器
*/

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    ngx_uint_t  flags;
    ngx_msec_t  timer, delta;

    if (ngx_timer_resolution) {
        timer = NGX_TIMER_INFINITE;
        flags = 0;

    } else {
        timer = ngx_event_find_timer();
        flags = NGX_UPDATE_TIME;
    }

    if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;

        } else {
            //获取ngx_use_accept_mutex,将套接字注册到I/O事件引擎上,事件类型accept
            if (ngx_trylock_accept_mutex(cycle)  NGX_ERROR) {
                return;
            }

            if (ngx_accept_mutex_held) {
                flags |= NGX_POST_EVENTS;

            } else {
                if (timer  NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    }

    delta = ngx_current_msec;

    '调用处理好的 read、write放到ngx_posted_events中,等待nginx处理'
    (void) ngx_process_events(cycle, timer, flags); 

    delta = ngx_current_msec - delta;

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

    //处理nginx事件队列中的事件
    ngx_event_process_posted(cycle, &ngx_posted_accept_events);

    if (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&ngx_accept_mutex);
    }

    if (delta) {
        ngx_event_expire_timers();
    }

    ngx_event_process_posted(cycle, &ngx_posted_events);
}
  • ngx_trylock_accept_mutex():调用
/**
    获取ngx_accept_mutex锁,添加事件
*/
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "accept mutex locked");

        if (ngx_accept_mutex_held && ngx_accept_events  0) {
            return NGX_OK;
        }

        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);

    if (ngx_accept_mutex_held) {
        if (ngx_disable_accept_events(cycle, 0)  NGX_ERROR) {
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 0;
    }

    return NGX_OK;
}
  • ngx_event_process_posted:nginx自己的处理事件列表中的事件
/**
    处理事件队列中的事件
*/
void
ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted)
{
    ngx_queue_t  *q;
    ngx_event_t  *ev;

    while (!ngx_queue_empty(posted)) {

        q = ngx_queue_head(posted);
        ev = ngx_queue_data(q, ngx_event_t, queue);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                      "posted event %p", ev);

        ngx_delete_posted_event(ev);

        ev->handler(ev);
    }
}
  • ngx_post_event:给nginx的事件队列添加事件
#define ngx_post_event(ev, q)                                                 \
                                                                              \
    if (!(ev)->posted) {                                                      \
        (ev)->posted = 1;                                                     \
        ngx_queue_insert_tail(q, &(ev)->queue);                               \
                                                                              \
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0, "post event %p", ev);\
                                                                              \
    } else  {                                                                 \
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0,                      \
                       "update posted event %p", ev);                         \
    }
  • ngx_enable_accept_events:将accept添加到I/O引擎中
static ngx_int_t
ngx_enable_accept_events(ngx_cycle_t *cycle)
{
    ngx_uint_t         i;
    ngx_listening_t   *ls;
    ngx_connection_t  *c;

    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {

        c = ls[i].connection;

        if (c  NULL || c->read->active) {
            continue;
        }
        '调用epoll的 ngx_epoll_add_event,添加套接字到I/O事件引擎上'
        if (ngx_add_event(c->read, NGX_READ_EVENT, 0)  NGX_ERROR) {
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}
  • I/O事件引擎完成accept类型事件时,read->handler= ngx_event_accept
/*
    nginx事件:用来处理I/O引擎中tcp的accept类型事件
    根据其它模块(http、stream、mail)设置的
*/
void
ngx_event_accept(ngx_event_t *ev)
{
    socklen_t          socklen;
    ngx_err_t          err;
    ngx_log_t         *log;
    ngx_uint_t         level;
    ngx_socket_t       s;
    ngx_event_t       *rev, *wev;
    ngx_sockaddr_t     sa;
    ngx_listening_t   *ls;
    ngx_connection_t  *c, *lc;
    ngx_event_conf_t  *ecf;

    if (ev->timedout) {
        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
            return;
        }

        ev->timedout = 0;
    }

    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);

    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
        ev->available = ecf->multi_accept;
    }

    lc = ev->data;
    ls = lc->listening;
    ev->ready = 0;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                   "accept on %V, ready: %d", &ls->addr_text, ev->available);

    do {
        socklen = sizeof(ngx_sockaddr_t);
        // 接受
        s = accept(lc->fd, &sa.sockaddr, &socklen); 

        if (s  (ngx_socket_t) -1) {
            err = ngx_socket_errno;

            if (err  NGX_EAGAIN) {
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
                               "accept() not ready");
                return;
            }

            level = NGX_LOG_ALERT;

            if (err  NGX_ECONNABORTED) {
                level = NGX_LOG_ERR;

            } else if (err  NGX_EMFILE || err  NGX_ENFILE) {
                level = NGX_LOG_CRIT;
            }

            ngx_log_error(level, ev->log, err, "accept() failed");
            if (err  NGX_ECONNABORTED) {
                if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
                    ev->available--;
                }

                if (ev->available) {
                    continue;
                }
            }

            if (err  NGX_EMFILE || err  NGX_ENFILE) {
                if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle, 1)
                    != NGX_OK)
                {
                    return;
                }

                if (ngx_use_accept_mutex) {
                    if (ngx_accept_mutex_held) {
                        ngx_shmtx_unlock(&ngx_accept_mutex);
                        ngx_accept_mutex_held = 0;
                    }

                    ngx_accept_disabled = 1;

                } else {
                    ngx_add_timer(ev, ecf->accept_mutex_delay);
                }
            }

            return;
        }

        ngx_accept_disabled = ngx_cycle->connection_n / 8
                              - ngx_cycle->free_connection_n;

        c = ngx_get_connection(s, ev->log);

        if (c  NULL) {
            if (ngx_close_socket(s)  -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                              ngx_close_socket_n " failed");
            }

            return;
        }

        c->type = SOCK_STREAM;

        c->pool = ngx_create_pool(ls->pool_size, ev->log);
        if (c->pool  NULL) {
            ngx_close_accepted_connection(c);
            return;
        }

        if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
            socklen = sizeof(ngx_sockaddr_t);
        }

        c->sockaddr = ngx_palloc(c->pool, socklen);
        if (c->sockaddr  NULL) {
            ngx_close_accepted_connection(c);
            return;
        }

        ngx_memcpy(c->sockaddr, &sa, socklen);

        log = ngx_palloc(c->pool, sizeof(ngx_log_t));
        if (log  NULL) {
            ngx_close_accepted_connection(c);
            return;
        }

        /* set a blocking mode for iocp and non-blocking mode for others */

        if (ngx_inherited_nonblocking) {
            if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
                if (ngx_blocking(s)  -1) {
                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                                  ngx_blocking_n " failed");
                    ngx_close_accepted_connection(c);
                    return;
                }
            }

        } else {
            if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
                if (ngx_nonblocking(s)  -1) {
                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                                  ngx_nonblocking_n " failed");
                    ngx_close_accepted_connection(c);
                    return;
                }
            }
        }

        *log = ls->log;

        c->recv = ngx_recv;
        c->send = ngx_send;
        c->recv_chain = ngx_recv_chain;
        c->send_chain = ngx_send_chain;

        c->log = log;
        c->pool->log = log;

        c->socklen = socklen;
        c->listening = ls;
        c->local_sockaddr = ls->sockaddr;
        c->local_socklen = ls->socklen;

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

        wev->ready = 1;

        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
            rev->ready = 1;
        }

        if (ev->deferred_accept) {
            rev->ready = 1;
        }

        rev->log = log;
        wev->log = log;

        /*
         * TODO: MT: - ngx_atomic_fetch_add()
         *             or protection by critical section or light mutex
         *
         * TODO: MP: - allocated in a shared memory
         *           - ngx_atomic_fetch_add()
         *             or protection by critical section or light mutex
         */

        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
        if (ls->addr_ntop) {
            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
            if (c->addr_text.data  NULL) {
                ngx_close_accepted_connection(c);
                return;
            }

            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
                                             c->addr_text.data,
                                             ls->addr_text_max_len, 0);
            if (c->addr_text.len  0) {
                ngx_close_accepted_connection(c);
                return;
            }
        }

        if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT)  0) {
            if (ngx_add_conn(c)  NGX_ERROR) {
                ngx_close_accepted_connection(c);
                return;
            }
        }

        log->data = NULL;
        log->handler = NULL;
        
        '根据其它模块(http、stream、mail)设置的'
        ls->handler(c); 

        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
            ev->available--;
        }

    } while (ev->available);
}


epoll I/O引擎模块
NGX_EVENT_MODULE的事件actions:
  • 特点:

    • 事件处理的函数集放在 cycle->ctx中
    • 事件的actions是一个全局变量:ngx_event_actions_t结构的ngx_event_actions
    • 每次actions.init()后会将,该module的 ngx_event_actions = cycle->ctx->actions
  • 例如:启用epoll模块事件结构:将所有的I/O操作都封装成一个nginx事件,然后在work中循环调用

ngx_event_module_t  ngx_epoll_module_ctx = {
    &epoll_name,
    ngx_epoll_create_conf,               /* create configuration */
    ngx_epoll_init_conf,                 /* init configuration */
    //nginx的事件处理函数
    {
        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 */
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};
epoll需要的函数函数
  • ngx_epoll_init() :事件启动函数,主要是初始化epoll模块
/**
    NGX_EVENT_MOUDLE中epoll的actions->init()函数
*/
static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
    ngx_epoll_conf_t  *epcf;
    //获取epoll的配置信息
    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);

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

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

    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;

    ngx_event_actions = ngx_epoll_module_ctx.actions;   //模块的总事件

    return NGX_OK;
}
  • ngx_epoll_add_event: 添加套接字到I/O事件引擎上
/**
    添加nginx事件(一个套接字)到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;

    c = ev->data;

    events = (uint32_t) event;

    if (event  NGX_READ_EVENT) {
        e = c->write;
        prev = EPOLLOUT;    //表示对应的文件描述符可以写;
    } else {
        e = c->read;
    }
    if (e->active) {
        op = EPOLL_CTL_MOD; //修改已经注册的fd的监听事件
        events |= prev;

    } else {
        op = EPOLL_CTL_ADD; //注册新的fd到epfd中;
    }
    ee.events = events | (uint32_t) flags;
    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);
    '将套接字添加到I/O引擎中'
    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;
    return NGX_OK;
}
    
  • ngx_epoll_process_events:epoll事件模块的I/O处理函数,将准备好的I/O事件封装到nginx事件队列中
/**
    I/O引擎事件处理函数
**/
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;
    ngx_queue_t       *queue;
    ngx_connection_t  *c;

    /* NGX_TIMER_INFINITE  INFTIM */

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "epoll timer: %M", timer);
    //获取epoll处理好的事件
    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();
    }

    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;
    }
    //循环处理
    for (i = 0; i < events; i++) {
        c = event_list[i].data.ptr;

        instance = (uintptr_t) c & 1;
        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 the error events were returned, add EPOLLIN and EPOLLOUT
             * to handle the events at least in one active handler
             */

            revents |= EPOLLIN|EPOLLOUT;
        }

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

            rev->ready = 1;

            if (flags & NGX_POST_EVENTS) {
                queue = rev->accept ? &ngx_posted_accept_events
                                    : &ngx_posted_events;

                '将准备好读事件添加到ngx_posted_accept_events和ngx_posted_events'
                ngx_post_event(rev, queue);
            } else {
                '如果不是NGX_POST_EVENTS,直接调用gx_posted_accept_events()和ngx_posted_events()'
                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;
            }

            wev->ready = 1;
            if (flags & NGX_POST_EVENTS) {
                '将准备好的写事件添加到ngx_posted_events'
                ngx_post_event(wev, &ngx_posted_events);  

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

    return NGX_OK;
}
http模块
  • 在work进程中,循环调用I/O事件模块的 ngx_process_events() 时。如果不是 NGX_POST_EVENTS类型的读I/O(即accept事件),则调用了ngx_event_accept()
  • 然后调用了listen->heandler = ngx_http_init_connection
ngx_http_init_connection:http连接的accept处理函数
  • 初始化ngx_http_connection_t
  • 设置read,write的处理函数
  • 将这个连接套接字放到I/O事件引擎上
/**
    http连接处理函数
    1.初始化ngx_http_connection_t
    2.设置read,write的处理函数
    3.将这个连接封装成一个nginx事件,添加到事件队列中
*/
void
ngx_http_init_connection(ngx_connection_t *c)
{
    ngx_uint_t              i;
    ngx_event_t            *rev;
    struct sockaddr_in     *sin;
    ngx_http_port_t        *port;
    ngx_http_in_addr_t     *addr;
    ngx_http_log_ctx_t     *ctx;
    ngx_http_connection_t  *hc;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6    *sin6;
    ngx_http_in6_addr_t    *addr6;
#endif
    //初始化http连接
    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
    if (hc  NULL) {
        ngx_http_close_connection(c);
        return;
    }
    //分配连接
    c->data = hc;

    /* find the server configuration for the address:port */
    //监听端口
    port = c->listening->servers;

    if (port->naddrs > 1) {

        /*
         * there are several addresses on this port and one of them
         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
         * is required to determine a server address
         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16)  0) {
                    break;
                }
            }

            hc->addr_conf = &addr6[i].conf;

            break;
#endif

        default: /* AF_INET */
            sin = (struct sockaddr_in *) c->local_sockaddr;
            //初始化地址
            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr  sin->sin_addr.s_addr) {
                    break;
                }
            }

            hc->addr_conf = &addr[i].conf;

            break;
        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            addr6 = port->addrs;
            hc->addr_conf = &addr6[0].conf;
            break;
#endif

        default: /* AF_INET */
            addr = port->addrs;
            hc->addr_conf = &addr[0].conf;
            break;
        }
    }

    /* the default server configuration for the address:port */
    hc->conf_ctx = hc->addr_conf->default_server->ctx;  //配置文件

    ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
    if (ctx  NULL) {
        ngx_http_close_connection(c);
        return;
    }

    ctx->connection = c;
    ctx->request = NULL;
    ctx->current_request = NULL;

    c->log->connection = c->number;
    c->log->handler = ngx_http_log_error;
    c->log->data = ctx;
    c->log->action = "waiting for request";

    c->log_error = NGX_ERROR_INFO;

    rev = c->read;  
    rev->handler = ngx_http_wait_request_handler;   //读事件
    c->write->handler = ngx_http_empty_handler;     //写事件

#if (NGX_HTTP_V2)
    if (hc->addr_conf->http2) {
        rev->handler = ngx_http_v2_init;
    }
#endif

#if (NGX_HTTP_SSL)
    {
    ngx_http_ssl_srv_conf_t  *sscf;

    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);

    if (sscf->enable || hc->addr_conf->ssl) {

        c->log->action = "SSL handshaking";

        if (hc->addr_conf->ssl && sscf->ssl.ctx  NULL) {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "no \"ssl_certificate\" is defined "
                          "in server listening on SSL port");
            ngx_http_close_connection(c);
            return;
        }

        hc->ssl = 1;

        rev->handler = ngx_http_ssl_handshake;  //SSL处理事件
    }
    }
#endif

    if (hc->addr_conf->proxy_protocol) {
        hc->proxy_protocol = 1;
        c->log->action = "reading PROXY protocol";
    }

    if (rev->ready) {
        /* the deferred accept(), iocp */

        if (ngx_use_accept_mutex) {
            ngx_post_event(rev, &ngx_posted_events);    ''
            return;
        }

        rev->handler(rev);  '直接执行read处理函数'
        return;
    }
    //设置结构的时间
    ngx_add_timer(rev, c->listening->post_accept_timeout);
    ngx_reusable_connection(c, 1);
    
    '将这个连接套接字放到I/O事件引擎上'
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        ngx_http_close_connection(c);
        return;
    }
}

ngx_http_wait_request_handler:http连接的read处理函数
  • 初始化ngx_http_connection_t
  • 设置buffer的大小
  • 测试连接是否可用
  • 如果有动态代理,则将其封装成一个事件放到nginx的事件队列中
  • 重新设置连接事件为:ngx_http_process_request_line
  • 解析读到的内容
/**
    http 连接的读事件
    1. 初始化ngx_http_connection_t
    2. 设置buffer的大小
    3. 测试连接是否可用
    3. 如果有动态代理,则将其封装成一个事件放到nginx的事件队列
    5. 重新设置连接事件为:ngx_http_process_request_line
    6. 解析读到的内容
*/
static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
    u_char                    *p;
    size_t                     size;
    ssize_t                    n;
    ngx_buf_t                 *b;
    ngx_connection_t          *c;
    ngx_http_connection_t     *hc;
    ngx_http_core_srv_conf_t  *cscf;
    //初始化ngx_connection_t
    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        ngx_http_close_connection(c);
        return;
    }

    if (c->close) {
        ngx_http_close_connection(c);
        return;
    }
    //初始化ngx_http_connection_t连接
    hc = c->data;
    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
    //初始化buffer的大小
    size = cscf->client_header_buffer_size;

    b = c->buffer;

    if (b  NULL) {
        b = ngx_create_temp_buf(c->pool, size);
        if (b  NULL) {
            ngx_http_close_connection(c);
            return;
        }

        c->buffer = b;

    } else if (b->start  NULL) {

        b->start = ngx_palloc(c->pool, size);
        if (b->start  NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = b->start;
        b->last = b->start;
        b->end = b->last + size;
    }
    //读取数据:用于测试连接是否可用
    n = c->recv(c, b->last, size);
    //如果出错
    if (n  NGX_AGAIN) {

        if (!rev->timer_set) {
            ngx_add_timer(rev, c->listening->post_accept_timeout);
            ngx_reusable_connection(c, 1);
        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        /*
         * We are trying to not hold c->buffer's memory for an idle connection.
         */

        if (ngx_pfree(c->pool, b->start)  NGX_OK) {
            b->start = NULL;
        }

        return;
    }

    if (n  NGX_ERROR) {
        ngx_http_close_connection(c);
        return;
    }

    if (n  0) {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client closed connection");
        ngx_http_close_connection(c);
        return;
    }

    b->last += n;
    //调用动态代理
    if (hc->proxy_protocol) {
        hc->proxy_protocol = 0;
        //返回动态代理的地址
        p = ngx_proxy_protocol_read(c, b->pos, b->last);

        if (p  NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = p;

        if (b->pos  b->last) {
            c->log->action = "waiting for request";
            b->pos = b->start;
            b->last = b->start;
            '将这个连接封装成一个nginx事件,添加到事件队列中'
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }
    }

    c->log->action = "reading client request line";

    ngx_reusable_connection(c, 0);

    c->data = ngx_http_create_request(c);
    if (c->data  NULL) {
        ngx_http_close_connection(c);
        return;
    }
    '重新设置事件的处理函数'
    rev->handler = ngx_http_process_request_line;
    '解析读到的内容'
    ngx_http_process_request_line(rev);
}
ngx_http_empty_handler:http连接的write处理函数

转载于:https://my.oschina.net/u/2246410/blog/1801304

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值