专栏首页原创分享nginx0.1.0之event模块初始化源码分析(3)

nginx0.1.0之event模块初始化源码分析(3)

前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历配置文件的内容,然后读取命令和参数。最后匹配nginx所有模块的配置,找到处理该指令的函数。我们首先看一下event模块中ngx_event_core_module模块的指令配置。

static ngx_command_t  ngx_event_core_commands[] = {

    { ngx_string("connections"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_connections,
      0,
      0,
      NULL },

    { ngx_string("use"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_use,
      0,
      0,
      NULL },

    { ngx_string("multi_accept"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_event_conf_t, multi_accept),
      NULL },

    { ngx_string("accept_mutex"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_event_conf_t, accept_mutex),
      &ngx_accept_mutex_post },

    { ngx_string("accept_mutex_delay"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
      0,
      offsetof(ngx_event_conf_t, accept_mutex_delay),
      NULL },

    { ngx_string("debug_connection"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_debug_connection,
      0,
      0,
      NULL },

      ngx_null_command
}

然后在看一下每个指令处理函数的代码。

connections指令

//把连接数记录在结构体中
static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd,
                                   void *conf)
{
    ngx_event_conf_t  *ecf = conf;

    ngx_str_t  *value;
    // 已经初始化过了
    if (ecf->connections != NGX_CONF_UNSET_UINT) {
        return "is duplicate" ;
    }
    // 从配置文件中解析出来的配置
    value = cf->args->elts;
    // 字符串转成整形
    ecf->connections = ngx_atoi(value[1].data, value[1].len);
    if (ecf->connections == (ngx_uint_t) NGX_ERROR) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid number \"%s\"", value[1].data);

        return NGX_CONF_ERROR;
    }

    cf->cycle->connection_n = ecf->connections;

    return NGX_CONF_OK;
}

use指令

// 记录use和name的值
static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_event_conf_t  *ecf = conf;

    ngx_int_t             m;
    ngx_str_t            *value;
    ngx_event_conf_t     *old_ecf;
    ngx_event_module_t   *module;

    if (ecf->use != NGX_CONF_UNSET_UINT) {
        return "is duplicate" ;
    }

    value = cf->args->elts;

    if (cf->cycle->old_cycle->conf_ctx) {
        old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,
                                     ngx_event_core_module);
    } else {
        old_ecf = NULL;
    }

    // 判断使用哪个事件驱动模块,比如use epoll,则使用epoll
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        if (module->name->len == value[1].len) {
            if (ngx_strcmp(module->name->data, value[1].data) == 0) {
                // 存储事件模块的下标,而不是字符串
                ecf->use = ngx_modules[m]->ctx_index;
                ecf->name = module->name->data;

                if (ngx_process == NGX_PROCESS_SINGLE
                    && old_ecf
                    && old_ecf->use != ecf->use)
                {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "when the server runs without a master process "
                        "the \"%s\" event type must be the same as "
                        "in previous configuration - \"%s\" "
                        "and it can not be changed on the fly, "
                        "to change it you need to stop server "
                        "and start it again",
                        value[1].data, old_ecf->name);

                    return NGX_CONF_ERROR;
                }

                return NGX_CONF_OK;
            }
        }
    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "invalid event type \"%s\"", value[1].data);

    return NGX_CONF_ERROR;
}

multi_accept指令

char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;

    ngx_str_t        *value;
    ngx_flag_t       *fp;
    ngx_conf_post_t  *post;

    fp = (ngx_flag_t *) (p + cmd->offset);

    if (*fp != NGX_CONF_UNSET) {
        return "is duplicate";
    }

    value = cf->args->elts;
    // on代表开启
    if (ngx_strcasecmp(value[1].data, "on") == 0) {
        *fp = 1;

    } else if (ngx_strcasecmp(value[1].data, "off") == 0) {
        *fp = 0;

    } else {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                     "invalid value \"%s\" in \"%s\" directive, "
                     "it must be \"on\" or \"off\"",
                     value[1].data, cmd->name.data);
        return NGX_CONF_ERROR;
    }
    // 设置完值后执行后置函数,一般是参数检查
    if (cmd->post) {
        post = cmd->post;
        return post->post_handler(cf, post, fp);
    }

    return NGX_CONF_OK;
}

accept_mutex指令(见上面的ngx_conf_set_flag_slot函数)

该指令配置了handle函数
ngx_conf_post_t  ngx_accept_mutex_post = { ngx_accept_mutex_check } 
static char *ngx_accept_mutex_check(ngx_conf_t *cf, void *post, void *data)
{
// 不支持该功能,重置字段
#if !(NGX_HAVE_ATOMIC_OPS)

    ngx_flag_t *fp = data;

    *fp = 0;

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                       "\"accept_mutex\" is not supported, ignored");

#endif

    return NGX_CONF_OK;
}

accept_mutex_delay指令

char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;

    ngx_msec_t       *msp;
    ngx_str_t        *value;
    ngx_conf_post_t  *post;


    msp = (ngx_msec_t *) (p + cmd->offset);
    if (*msp != NGX_CONF_UNSET_MSEC) {
        return "is duplicate";
    }

    value = cf->args->elts;
    // 把字符串解析成时间
    *msp = ngx_parse_time(&value[1], 0);
    if (*msp == (ngx_msec_t) NGX_ERROR) {
        return "invalid value";
    }

    if (*msp == (ngx_msec_t) NGX_PARSE_LARGE_TIME) {
        return "value must be less than 597 hours";
    }

    if (cmd->post) {
        post = cmd->post;
        return post->post_handler(cf, post, msp);
    }

    return NGX_CONF_OK;
}

debug_connection指令

static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd,
                                        void *conf)
{
// 配置后需要开启debug参数
#if (NGX_DEBUG)
    ngx_event_conf_t  *ecf = conf;

    in_addr_t       *addr;
    ngx_str_t       *value;
    struct hostent  *h;

    value = cf->args->elts;

    /* AF_INET only */

    if (!(addr = ngx_push_array(&ecf->debug_connection))) {
        return NGX_CONF_ERROR;
    }
    // 将ip转成长整型
    *addr = inet_addr((char *) value[1].data);
    // 转成功则返回
    if (*addr != INADDR_NONE) {
        return NGX_OK;
    }
    // 没转成功可能是一个主机字符串,获取该主机对应的ip信息
    h = gethostbyname((char *) value[1].data);

    if (h == NULL || h->h_addr_list[0] == NULL) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "host %s not found", value[1].data);
        return NGX_CONF_ERROR;
}

    *addr = *(in_addr_t *)(h->h_addr_list[0]);

#else

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                       "\"debug_connection\" is ignored, you need to rebuild "
                       "nginx using --with-debug option to enable it");

#endif

    return NGX_OK;
}

还有事件驱动模块的一些配置,比如epoll模块的epoll_events指令,几乎都是简单地对相应模块的配置结构体的某个字段进行赋值。

到这里,event模块关于配置文件解析的部分就结束了。主要就是对下图中最右边的那个数组里的结构体进行赋值。event各子模块的init_conf函数的功能就是,如果解析配置文件的时候,没有对下图右侧的结构体相应的字段进行赋值,那init_conf函数就初始化一个默认的值。

如epoll的init_conf函数如下。

static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf)
{
    ngx_epoll_conf_t *epcf = conf;

    ngx_conf_init_unsigned_value(epcf->events, 512);

    return NGX_CONF_OK;
}

初始化完配置后,nginx执行各模块的init_module函数,event模块中只有ngx_core_module子模块实现了该函数。代码如下。

static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle)
{
#if !(WIN32)

    size_t             size;
    char              *shared;
    ngx_core_conf_t   *ccf;
    ngx_event_conf_t  *ecf;

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

    if (ccf->master == 0 || ngx_accept_mutex_ptr) {
        return NGX_OK;
    }
    // 获取ngx_event_core_module模块的配置,即ngx_event_conf_t结构体
    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);


    /* TODO: 128 is cache line size */

    size = 128            /* ngx_accept_mutex */
           + 128;         /* ngx_connection_counter */

#if (NGX_STAT_STUB)

    size += 128           /* ngx_stat_accepted */
           + 128          /* ngx_stat_requests */
           + 128          /* ngx_stat_active */
           + 128          /* ngx_stat_reading */
           + 128;         /* ngx_stat_writing */

#endif
    // 创建进程间共享的内存
    if (!(shared = ngx_create_shared_memory(size, cycle->log))) {
        return NGX_ERROR;
    }

    ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
    ngx_connection_counter = (ngx_atomic_t *) (shared + 128);

#if (NGX_STAT_STUB)

    ngx_stat_accepted = (ngx_atomic_t *) (shared + 2 * 128);
    ngx_stat_requests = (ngx_atomic_t *) (shared + 3 * 128);
    ngx_stat_active = (ngx_atomic_t *) (shared + 4 * 128);
    ngx_stat_reading = (ngx_atomic_t *) (shared + 5 * 128);
    ngx_stat_writing = (ngx_atomic_t *) (shared + 6 * 128);

#endif

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "counter: " PTR_FMT ", %d",
                   ngx_connection_counter, *ngx_connection_counter);

#endif

    return NGX_OK;
}

本文分享自微信公众号 - 编程杂技(theanarkh)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • nginx1.17.9源码分析之线程池

    我们发现事件驱动的软件都得配一个线程池。libuv和nginx都是。因为事件驱动的软件是单线程。但是有些事情总会引起线程阻塞。所以这个事情就不能放到主线程里做。...

    theanarkh
  • nginx1.17.9源码分析之管理配置的结构体(1)

    之前对nginx0.1.0版本进行了部分代码的分析,接下来的时间,打算以最新版的源码为基础,重新开始分析nginx的实现。这是第一篇。

    theanarkh
  • nginx0.1.0之event模块初始化源码分析(1)

    nginx模块初始化的流程在下面的代码中,核心模块的初始化,各核心模块首先在create_conf中创建保存配置的数据结构,然后在ngx_conf_parse中...

    theanarkh
  • nginx lua api解读

    标识response结束,ngx.eof()只是结束响应流的输出,中断HTTP连接,后面的代码逻辑还会继续在服务端执行

    codecraft
  • nginx1.17.9源码分析之管理配置的结构体(1)

    之前对nginx0.1.0版本进行了部分代码的分析,接下来的时间,打算以最新版的源码为基础,重新开始分析nginx的实现。这是第一篇。

    theanarkh
  • nginx0.1.0之http模块初始化源码分析(2)

    本文讲解http各个模块create_srv_conf和create_loc_conf钩子,还有指令的解析。 各模块的create_srv_conf和creat...

    theanarkh
  • 从nginx1.17.9源码理解nginx -s reload

    使用nginx的时候,我们经常会使用nginx -s reload命令重启。下面我们就分析一下,执行这个命令的时候,nginx里发生了什么?我们从nginx的m...

    theanarkh
  • Nginx vs Envoy vs Mosn 平滑升级原理解析

    本文适合对 Nginx 实现原理比较感兴趣的同学阅读,需要具备一定的网络编程知识。

    poslua
  • WebDriver自动化项目设计模式快速入门-自动化测试系列笔记

    朵拉小序:Page Object,即”页面对象“,就是将单个页面作为一个对象来处理。 以面向对象的方式来处理页面和业务流程的好处在于,如果某个页面元素的属性有了...

    企鹅号小编
  • 速读原著-TCP/IP(往返时间测量)

    T C P超时与重传中最重要的部分就是对一个给定连接的往返时间( RT T)的测量。由于路由器和网络流量均会变化,因此我们认为这个时间可能经常会发生变化, T ...

    cwl_java

扫码关注云+社区

领取腾讯云代金券