zoukankan      html  css  js  c++  java
  • Nginx源码研究三:Epoll在NGINX中的使用

         Web服务器在面对高并发的情况下,网络的IO一般选择IO复用,像apache选择的Select/poll。Nginx在linux 2.6后选择Epoll做网路IO,提高了WEB服务的并发能力。

    在本章,我们将看看NGINX如何使用epoll。

    首先,我们看一下数据结构图:

    1、从结构图中,我们先看第一部分,NGINX拿到socket标示符,绑定本地地址,监听socket标示符信息,由于NGINX支持多server,支持各个server使用不同的端口、不同的协议族。如此需要多个socket句柄来支持,NGINX利用ngx_listening_t这个数据结构来保存打开的socket信息并且放置在一个数组里面。这个过程是在初始化cycle中完成的。

    2、关于socket句柄,我们区分两类,一类是上一部分说的服务端申请的socket,这一部分socket处理和客户端连接的情况,如果有新的连接进来,将会产生关于这一批socket的事件;第二类,是连接建立后,服务端accept客户端的socket句柄,针对这个句柄,服务端做读和写的操作。

    我们再看一下下图绘制的一个处理流程。

    我们再通过代码来看看整个过程

    分析配置文件,申请socket fd

    2、处理事件模块的初始化

    static ngx_int_t
    ngx_event_process_init(ngx_cycle_t *cycle)
    {
        .......
        
        //对所有NGX_EVENT_MODULE模块执行module->actions.init
        for (m = 0; ngx_modules[m]; m++) {
            if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
                continue;
            }
    
            if (ngx_modules[m]->ctx_index != ecf->use) {
                continue;
            }
    
            module = ngx_modules[m]->ctx;
    
            if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
                /* fatal */
                exit(2);
            }
    
            break;
        }
    
        ......
        
        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;
    #if (NGX_THREADS)
            rev[i].lock = &c[i].lock;
            rev[i].own_lock = &c[i].lock;
    #endif
        }
    
        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;
    #if (NGX_THREADS)
            wev[i].lock = &c[i].lock;
            wev[i].own_lock = &c[i].lock;
    #endif
        }
    
        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];
    
    #if (NGX_THREADS)
            c[i].lock = 0;
    #endif
        } 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++) {
    
            //获取空余连接,将服务端申请的每个socket fd占用一个连接,
            c = ngx_get_connection(ls[i].fd, cycle->log);
    
            ......
            rev->handler = ngx_event_accept;
    
            if (ngx_use_accept_mutex) {
                continue;
            }
    
            if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
                if (ngx_add_conn(c) == NGX_ERROR) {
                    return NGX_ERROR;
                }
    
            } else {
                //如果使用epoll模块,则在epoll上注册读事件,操作类型是EPOLL_CTL_ADD,也就是关注服务端申请的socket fd是否有新连接申请
                if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                    return NGX_ERROR;
                }
            }
    
        }
    
        return NGX_OK;
    }

    3、  EPOLL在发现新事件后,会查找处理该事件的方法,实际上先查找对应的连接,该连接指针是如下方式,利用epoll_event结构体成员data的ptr指针可以用来存储用户的连接信息,我们在看看后面是怎么处理的

    static ngx_int_t
    ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
    {
        .......
        //epoll_event结构体成员data的ptr指针可以用来存储用户的指针信息,这里用来存储连接信息,其中事件的instance标识主要用来显示事件是否过期
        
        ee.events = events | (uint32_t) flags;
        ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); //把instance加到ee的末尾(由于内存对齐,末尾一般为0)
    
    …….
    
        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;
        }
        .......
    
        return NGX_OK;
    }

    4、

    static void
    ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
    {
        ......
    
        ngx_worker_process_init(cycle, worker);
    
        ......
    
        for ( ;; ) {
    
            ......
            ngx_process_events_and_timers(cycle);
            ......
        }
    }

    5、

    void
    ngx_process_events_and_timers(ngx_cycle_t *cycle)
    {
        ......
        (void) ngx_process_events(cycle, timer, flags);
        ......
     
    }
  • 相关阅读:
    汇编指令记录
    nginx源码剖析(3)nginx中的内存池
    STL中的vector
    Direct3D学习笔记
    委托的作用
    vs2010 快捷键大全
    Web Service学习笔记:什么是Web Service
    [Serializable]在C#中的作用NET 中的对象序列化
    .NET中反射机制的使用与分析
    什么时候用WebService
  • 原文地址:https://www.cnblogs.com/yimuren/p/4105124.html
Copyright © 2011-2022 走看看