Nginx源码分析-Epoll模块

Linux平台上,Nginx使用epoll完成事件驱动,实现高并发;本文将不对epoll本身进行介绍(网上一堆一堆的文章介绍epoll的原理及使用方法,甚至源码分析等),仅看一下Nginx是如何使用epoll的。

Nginx在epoll模块中定义了好几个函数,这些函数基本都是作为回调注册到事件抽象层的对应接口上,从而实现了事件驱动的具体化,我们看如下的一段代码:

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 */
    }
};

这段代码就是epoll的相关函数注册到事件抽象层,这里所谓的事件抽象层在前面的博文中有提过,就是Nginx为了方便支持和开发具体的I/O模型,从而实现的一层抽象。代码后面的注释将功能说明得很详细了,本文就只重点关注ngx_epoll_init和ngx_epoll_process_events两个函数,其他几个函数就暂且忽略了。

ngx_epoll_init主要是完成epoll的相关初始化工作,代码分析如下:

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);
         /*ep是epoll模块定义的一个全局变量,初始化为-1*/
    if (ep == -1) {
         /*创一个epoll对象,容量为总连接数的一半*/
        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;
        }
    }
         /*nevents也是epoll模块定义的一个全局变量,初始化为0*/
    if (nevents events) {
        if (event_list) {
            ngx_free(event_list);
        }

                 /*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_is定义为:
                 ngx_os_io_t ngx_os_io = {
                 ngx_unix_recv,
                 ngx_readv_chain,
                 ngx_udp_unix_recv,
                 ngx_unix_send,
                 ngx_writev_chain,
                 0
                 };(位于src/os/unix/ngx_posix_init.c)
         */
    ngx_io = ngx_os_io;
         /*这里就是将epoll的具体接口函数注册到事件抽象层接口ngx_event_actions上。
         具体是上文提到的ngx_epoll_module_ctx中封装的如下几个函数
        ngx_epoll_add_event,
        ngx_epoll_del_event,
        ngx_epoll_add_event,
        ngx_epoll_del_event,
        ngx_epoll_add_connection,
        ngx_epoll_del_connection,
        ngx_epoll_process_events,
        ngx_epoll_init,
        ngx_epoll_done,
         */
    ngx_event_actions = ngx_epoll_module_ctx.actions;
#if (NGX_HAVE_CLEAR_EVENT)
         /*epoll将添加这个标志,主要为了实现边缘触发*/
    ngx_event_flags = NGX_USE_CLEAR_EVENT
#else
         /*水平触发*/
    ngx_event_flags = NGX_USE_LEVEL_EVENT
#endif
                      |NGX_USE_GREEDY_EVENT /*io的时候,直到EAGAIN为止*/
                      |NGX_USE_EPOLL_EVENT; /*epoll标志*/
    return NGX_OK;
}

epoll初始化工作没有想象中的复杂,和我们平时使用epoll都一样,下面看ngx_epoll_process_events,这个函数主要用来完成事件的等待并处理。

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_log_t         *log;
    ngx_event_t       *rev, *wev, **queue;
    ngx_connection_t  *c;
         /*一开始就是等待事件,最长等待时间为timer;nginx为事件
         专门用红黑树维护了一个计时器。后续对这个timer单独分析。
         */
    events = epoll_wait(ep, event_list, (int) nevents, timer);
    if (events == -1) {
        err = ngx_errno;
    } else {
        err = 0;
    }
    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
        /*执行一次时间更新, nginx将时间缓存到了一组全局变量中,方便程序高效的获取事件。*/
        ngx_time_update();
    }
         /*处理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;
    }
         /*wait返回事件数0,可能是timeout返回,也可能是非timeout返回;非timeout返回则是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;
    }
    log = cycle->log;
         /*for循环开始处理收到的所有事件*/
    for (i = 0; i read;
                 。。。。。。。。。。。。。

                 /*取得发生一个事件*/
        revents = event_list[i].events;

                 /*记录wait的错误返回状态*/
        if (revents & (EPOLLERR|EPOLLHUP)) {
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
                           "epoll_wait() error on fd:%d ev:%04XD",
                           c->fd, revents);
        }
        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;
        }
                 /*该事件是一个读事件,并该连接上注册的读事件是active的*/
        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) {
                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 (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);
            }
        }
    }
    return NGX_OK;
}

本文将关注的两个epoll函数也就这么一点代码了,但整个epoll还有添加事件和删除事件等的相关函数,代码都很简单,本文就不做具体的分析了。

 

时间: 2024-07-29 14:13:24

Nginx源码分析-Epoll模块的相关文章

Nginx源码分析1--------编写Nginx扩展模块

近日来申请通过CSDN准专家,为了顺利在六个月后升级为认证专家,并对得起这个勋章,我感觉 不能松懈博客的更新频率以及质量了.C/C++ windows下的开发是我相对来说做的比较多的地方,对于Linux下的服务器开发 等等也算是半路出家,恰逢近来在研究分布式存储,涉及到了 Nginx 扩展开发以及配置,查阅了好多的资料发现Nginx配置部署起来相当的容易,但是源代码是真的晦涩难懂,经常会看的我们百思不得其解,所以扩展开发Nginx模块也不是一个轻松的事情,那我就从Nginx扩展模块开始,一步一步

nginx线程池源码分析_nginx

周末看了nginx线程池部分的代码,顺手照抄了一遍,写成了自己的版本.实现上某些地方还是有差异的,不过基本结构全部摘抄. 在这里分享一下.如果你看懂了我的版本,也就证明你看懂了nginx的线程池. 本文只列出了关键数据结构和API,重在理解nginx线程池设计思路.完整代码在最后的链接里. 1.任务节点 typedef void (*CB_FUN)(void *); //任务结构体 typedef struct task { void *argv; //任务函数的参数(任务执行结束前,要保证参数

《深入剖析Nginx》——第1章 源码分析的准备工作1.1 主要特性

第1章 源码分析的准备工作 从Nginx(读作engine x)的官方网站,我们可以看到如下介绍:Nginx是Igor Sysoev编写的一款HTTP和反向代理服务器,另外它也可以当作邮件代理服务器.它一直被众多流量巨大的俄罗斯网站所使用,例如Yandex.Mail.Ru.VKontakte以及Rambler等.据Netcraft统计,截止到2012年8月份,世界上最繁忙的网站中有11.48%在使用Nginx作为其服务器或者代理服务器.部分典型成功案例有:Netflix.Wordpress.co

Epoll详解及源码分析【转】

  转自:http://blog.csdn.net/chen19870707/article/details/42525887 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-]   什么是epoll   Apache模型Process Per Connection简称PPC 和 TPCThread Per Connection模型 select模型 poll模型 epoll模型 Epoll API int epoll_createint size int epoll_c

《深入剖析Nginx》一第1章 源码分析的准备工作

第1章 源码分析的准备工作 深入剖析Nginx 从Nginx(读作engine x)的官方网站1,我们可以看到如下介绍:Nginx是Igor Sysoev2编写的一款HTTP和反向代理服务器,另外它也可以当作邮件代理服务器.它一直被众多流量巨大的俄罗斯网站所使用,例如Yandex3.Mail.Ru4.VKontakte5以及Rambler6等.据Netcraft统计,截止到2012年8月份,世界上最繁忙的网站中有11.48%7在使用Nginx作为其服务器或者代理服务器.部分典型成功案例有:Net

《深入剖析Nginx》一1.4 源码分析工具

1.4 源码分析工具 深入剖析Nginx 对于Windows平台,首选Source Insight1源码阅读工具.该工具功能强大,根据其官方网站的介绍,Source Insight是一款面向项目开发的程序编辑器和代码浏览器,它拥有内置的对C/C++.C#和Java等程序的分析功能.Source Insight能自动分析和动态维护源码工程的符号数据库,并在用户查看代码时显示有用的对应上下文信息. 如果是在Linux平台下,则可以利用Vi2.Taglist3.Cscope4以及Ctag5这几个工具来

Hadoop2源码分析-HDFS核心模块分析

1.概述 这篇博客接着<Hadoop2源码分析-RPC机制初识> 来讲述,前面我们对MapReduce.序列化.RPC进行了分析和探索,对Hadoop V2的这些模块都有了大致的了解,通过对这些模块的研究,我们明白了MapReduce的运行流程以及内部的实现机制,Hadoop的序列化以及它的通信 机制(RPC).今天我们来研究另一个核心的模块,那就是Hadoop的分布式文件存储系统--HDFS,下面是今天分享的内容目录: HDFS简述 NameNode DataNode 接下来,我们开始今天的

《深入剖析Nginx》——1.4 源码分析工具

1.4 源码分析工具 对于Windows平台,首选Source Insight1源码阅读工具.该工具功能强大,根据其官方网站的介绍,Source Insight是一款面向项目开发的程序编辑器和代码浏览器,它拥有内置的对C/C++.C#和Java等程序的分析功能.Source Insight能自动分析和动态维护源码工程的符号数据库,并在用户查看代码时显示有用的对应上下文信息. 如果是在Linux平台下,则可以利用Vi2.Taglist3.Cscope4以及Ctag5这几个工具来组合成阅读Nginx

epoll源码分析

epoll源码分析 作者 digoal 日期 2016-11-10 标签 Linux , 内核 , epoll , 网络编程 , 高并发 背景 本文转自 http://www.cnblogs.com/debian/archive/2012/02/16/2354454.html 原文 当系统启动时,epoll进行初始化: 1 static int __init eventpoll_init(void) 2 { 3 mutex_init(&pmutex); 4 ep_poll_safewake_in