菜鸟学习nginx之核心模块ngx_events_module

既然Nginx是HTTP服务端软件,那么必然离不开网络。今天就介绍Nginx高性能网络是如何实现的。

一、核心模块-ngx_events_module (校长)

上一篇中介绍了Nginx模块抽象,今天深入探讨一下ngx_event_module事件模块。

1.1、模块定义

/**
 * 模块定义--事件模块
 */
ngx_module_t ngx_events_module = {
    NGX_MODULE_V1,
    &ngx_events_module_ctx, /* module context */
    ngx_events_commands,    /* module directives */
    NGX_CORE_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};

说明:

1)ngx_events_module类型为NGX_CORE_MODULE属于核心模块, 可见type赋值

2)模块上下文ctx赋值为ngx_events_module_ctx。其实所有核心模块(NGX_CORE_MODULE)的ctx指向类型都是ngx_core_module_t。

1.2、模块上下文

事件模块上下文定义为ngx_events_module_ctx,具体如下:

/**
 * 核心模块(NGX_CORE_MODULE),上下文
 * 所有核心模块的上下均为该接口
 */
typedef struct {
    ngx_str_t             name; /* 与配置文件nginx.conf中出现的标签保持一致 */
    void               *(*create_conf)(ngx_cycle_t *cycle);
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

/**
 * 定义模块上下文,当我们新增核心模块时,需要指定ngx_core_module_t作为上下文
 */
static ngx_core_module_t ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,
    ngx_event_init_conf};

说明:

1)所有核心模块上下文均抽象为ngx_core_module_t,例如:HTTP模块ngx_http_module 。

2)name名称,当在nginx.conf文件出现与name相匹配的,则表示是该模块。如下所示:

//nginx.conf配置文件
events {
    worker_connections  1024;
}

1.3、命令字解析

当出现上面定义的标签,那么Nginx是如何解析该标签呢?可参考命令字结构体

/**
 * 命令字解析
 */
struct ngx_command_s {
    ngx_str_t             name; /* 标签名字 即出现nginx*/
    ngx_uint_t            type; /* 该命令字所属范围 */
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* 解析命令字回调函数 */
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};

/** 
 * 解析命令字 即解析配置文件 当配置文件出现标签events时调用ngx_events_block方法 
 */
static ngx_command_t ngx_events_commands[] = {

    {ngx_string("events"),
     NGX_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS,
     ngx_events_block,
     0,
     0,
     NULL},

    ngx_null_command};

字段名称

含义

备注

name

出现在配置文件中配置项名字

必须指定

type

当前配置项所属范围

1、可取值NGX_HTTP_MAIN_CONF,NGX_HTTP_SRV_CONF,

NGX_CONF_NOARGS等。

2、可有多值,当存在多值需进行或操作,必须指定

set

回调方法,主要用于解析当前项

必须指定

conf

表示存储位置

1、开发非http模块时设置为0

2、基于http模块开发时,可取值为NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET中之一。举例说明:若存储该配置项的数据结构是由回调函数create_main_conf(struct ngx_http_module_t)生成则设置为NGX_HTTP_MAIN_CONF_OFFSE

offset

当前配置项,在数据结构中偏移量

1、一般场景下,一个配置项会对应结构体中一个变量,此处offset取值就是该变量相对于结构体起始地址的偏移量

2、必须指定

post

一般不用设置为NULL

 

说明:

当配置文件中出现events标签则调用ngx_events_block回调进行解析。 

1.4、模块加载流程

简单回顾一下Nginx启动流程。Nginx启动时候会进入ngx_init_cycle函数,在这个函数中会初始化module,部分代码如下:

    /* 初始化核心模块即类型为NGX_CORE_MODULE */
    for (i = 0; cycle->modules[i]; i++)
    {
        if (cycle->modules[i]->type != NGX_CORE_MODULE)
        {
            continue;
        }
            
        module = cycle->modules[i]->ctx;/* 定义模块时赋值 */

        if (module->create_conf)
        {
            /**
             * 目前定义create_conf回调方法 只有ngx_core_module和ngx_regex_module
             * ngx_event_module没有定义create_conf,只定义了init_conf,可知
             * ngx_event_module对应的conf_ctx是NULL, 但是在经过ngx_conf_parse后
             * conf_ctx不为NULL. 
             */
            rv = module->create_conf(cycle);
            if (rv == NULL)
            {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[cycle->modules[i]->index] = rv;//给指针数组赋值
        }
    }

    ...

    if (ngx_conf_param(&conf) != NGX_CONF_OK)
    {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }
    /**
     * 解析配置文件 
     * 注1: 经过此方法之后 核心模块ngx_event_module对应的conf_ctx有数据了 
     * 在执行ngx_conf_parse函数时,会解析nginx.conf配置文件,当遇到event标签,会调用
     * ngx_events_block回调方法 该方法会设置conf_ctx
     * 注2: 经过此方法cycle->listening中会保存真正数据
     */
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK)
    {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }

    if (ngx_test_config && !ngx_quiet_mode)
    {
        ngx_log_stderr(0, "the configuration file %s syntax is ok",
                       cycle->conf_file.data);
    }

    for (i = 0; cycle->modules[i]; i++)
    {
        if (cycle->modules[i]->type != NGX_CORE_MODULE)
        {
            continue;
        }

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

        if (module->init_conf)
        {
            if (module->init_conf(cycle,
                                  cycle->conf_ctx[cycle->modules[i]->index]) == NGX_CONF_ERROR)
            {
                environ = senv;
                ngx_destroy_cycle_pools(&conf);
                return NULL;
            }
        }
    }

通过上面代码可知,会分别调用核心模块中定义的create_conf和init_conf回调函数。针对于ngx_events_module来看,只实现了init_conf回调函数,ngx_events_module并没有执行具体逻辑,只是单纯检查了一下,代码如下:

static char *
ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
{
    if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL)
    {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                      "no \"events\" section in configuration");
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}

 那么什么时候调用ngx_events_block呢?初学者不太清楚的话,有一个技巧可以帮助我们--GDB。通过GDB打断点,进行调试如下所示:


[root@localhost ~]# gdb /usr/local/nginx/sbin/nginx
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-ZTEOS-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/local/nginx/sbin/nginx...done.
(gdb) b ngx_events_block
Breakpoint 1 at 0x429a8b: file src/event/ngx_event.c, line 965.
(gdb) set follow-fork-mode child
(gdb) r
Starting program: /usr/local/nginx/sbin/nginx
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, ngx_events_block (cf=0x7fffffffe180, cmd=0x69f960 <ngx_events_commands>, conf=0x6d2070) at src/event/ngx_event.c:965
965     src/event/ngx_event.c: No such file or directory.
Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7_3.4.cgsl1342.x86_64 nss-softokn-freebl-3.28.3-8.el7_4.x86_64 pcre-8.32-17.el7.x86_64 zlib-1.2.7-17.el7.x86_64
(gdb) bt
#0  ngx_events_block (cf=0x7fffffffe180, cmd=0x69f960 <ngx_events_commands>, conf=0x6d2070) at src/event/ngx_event.c:965
#1  0x000000000041ff88 in ngx_conf_handler (last=1, cf=0x7fffffffe180) at src/core/ngx_conf_file.c:500
#2  ngx_conf_parse (cf=cf@entry=0x7fffffffe180, filename=filename@entry=0x6d1270) at src/core/ngx_conf_file.c:333
#3  0x000000000041d704 in ngx_init_cycle (old_cycle=old_cycle@entry=0x7fffffffe230) at src/core/ngx_cycle.c:293
#4  0x000000000040c7e3 in main (argc=<optimized out>, argv=<optimized out>) at src/core/nginx.c:284
(gdb)

通过堆栈可知,是在执行ngx_init_cycle函数时调用ngx_conf_parse,最终调用到ngx_events_block。其实就是在解析配置文件时。接下来看一下ngx_events_block函数定义:

/**
 * 针对events标签进行解析
 * @param cf  配置结构
 * @param cmd 命令字映射表
 * @param conf 配置结构
 */
static char *
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char *rv;
    void ***ctx;
    ngx_uint_t i;
    ngx_conf_t pcf;
    ngx_event_module_t *m;

    if (*(void **)conf)
    {
        return "is duplicate";
    }

    /* count the number of the event modules and set up their indices */
    /* 获取所有event模块数量并且设置他们的索引值ctx_index             */
    ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);

    ctx = ngx_pcalloc(cf->pool, sizeof(void *));
    if (ctx == NULL)
    {
        return NGX_CONF_ERROR;
    }

    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    if (*ctx == NULL)
    {
        return NGX_CONF_ERROR;
    }

    *(void **)conf = ctx;
    /* 调用模块的create_conf方法,模块类型必须是NGX_EVENT_MODULE */
    for (i = 0; cf->cycle->modules[i]; i++)
    {
        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE)
        {
            continue;
        }

        m = cf->cycle->modules[i]->ctx;

        if (m->create_conf)
        {// ctx_index是在上方ngx_count_modules中设置
            (*ctx)[cf->cycle->modules[i]->ctx_index] =
                m->create_conf(cf->cycle);
            if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL)
            {
                return NGX_CONF_ERROR;
            }
        }
    }

    pcf = *cf;
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK)
    {
        return rv;
    }
    /* 调用模块的init_conf方法,模块类型必须是NGX_EVENT_MODULE */
    for (i = 0; cf->cycle->modules[i]; i++)
    {
        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE)
        {
            continue;
        }

        m = cf->cycle->modules[i]->ctx;

        if (m->init_conf)
        {
            rv = m->init_conf(cf->cycle,
                              (*ctx)[cf->cycle->modules[i]->ctx_index]);
            if (rv != NGX_CONF_OK)
            {
                return rv;
            }
        }
    }

    return NGX_CONF_OK;
}

 这个地方需要特别注意,这里调用create_conf和init_conf回调函数,是类型为NGX_EVENT_MODULE的模块所定义的。在Linux环境下,调用的是ngx_event_core_module、ngx_epoll_module所定义的。

二、事件模块-ngx_event_core_module(班主任)

上面介绍了,核心模块ngx_events_module,接下来介绍事件模块(NGX_EVENT_MODULE)中ngx_event_core_module。虽然名字中有core,但并不是核心模块。

2.1、模块定义

static ngx_str_t event_core_name = ngx_string("event_core");

static ngx_command_t ngx_event_core_commands[] = {

    {ngx_string("worker_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_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(ngx_event_conf_t, multi_accept),
     NULL},

    {ngx_string("accept_mutex"),
     NGX_EVENT_CONF | NGX_CONF_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(ngx_event_conf_t, accept_mutex),
     NULL},

    {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};

static ngx_event_module_t ngx_event_core_module_ctx = {
    &event_core_name,
    ngx_event_core_create_conf, /* create configuration */
    ngx_event_core_init_conf,   /* init configuration */

    {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};

ngx_module_t ngx_event_core_module = {
    NGX_MODULE_V1,
    &ngx_event_core_module_ctx, /* module context */
    ngx_event_core_commands,    /* module directives */
    NGX_EVENT_MODULE,           /* module type */
    NULL,                       /* init master */
    ngx_event_module_init,      /* init module */
    ngx_event_process_init,     /* init process */
    NULL,                       /* init thread */
    NULL,                       /* exit thread */
    NULL,                       /* exit process */
    NULL,                       /* exit master */
    NGX_MODULE_V1_PADDING};

2.2、模块实现

对于上面ngx_event_core_module模块,上面回调函数调用先后顺序为:create_conf,init_conf,init_module,init_process。下面来看一下,4个回调函数具体实现内容:

/**
 * 创建event conf结构
 * @param cycle 核心结构体
 */
static void *
ngx_event_core_create_conf(ngx_cycle_t *cycle)
{
    ngx_event_conf_t *ecf;

    ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
    if (ecf == NULL)
    {
        return NULL;
    }
    /* 与命令字保持一致 初始化为-1*/
    ecf->connections = NGX_CONF_UNSET_UINT;
    ecf->use = NGX_CONF_UNSET_UINT;
    ecf->multi_accept = NGX_CONF_UNSET;
    ecf->accept_mutex = NGX_CONF_UNSET;
    ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
    ecf->name = (void *)NGX_CONF_UNSET;

#if (NGX_DEBUG)

    if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
                       sizeof(ngx_cidr_t)) == NGX_ERROR)
    {
        return NULL;
    }

#endif

    return ecf;
}
static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
    ngx_event_conf_t *ecf = conf;
    /* linux下面使用epoll模型 */
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
    int fd;
#endif
    ngx_int_t i;
    ngx_module_t *module;
    ngx_event_module_t *event_module;

    module = NULL;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

    fd = epoll_create(100); //创建epoll对象 此处100没有实际意义

    if (fd != -1)
    {
        (void)close(fd);//此处只是表示epoll模型可用 在ngx_epoll_init函数中真正创建
        module = &ngx_epoll_module;
    }
    else if (ngx_errno != NGX_ENOSYS)
    {
        module = &ngx_epoll_module;
    }

#endif

#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)

    module = &ngx_devpoll_module;

#endif

#if (NGX_HAVE_KQUEUE)

    module = &ngx_kqueue_module;

#endif

#if (NGX_HAVE_SELECT)
    /* windows下面使用select模型 */
    if (module == NULL)
    {
        module = &ngx_select_module;
    }

#endif

    if (module == NULL)
    {
        for (i = 0; cycle->modules[i]; i++)
        {

            if (cycle->modules[i]->type != NGX_EVENT_MODULE)
            {
                continue;
            }

            event_module = cycle->modules[i]->ctx;

            if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
            {
                continue;
            }

            module = cycle->modules[i];
            break;
        }
    }

    if (module == NULL)
    {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
        return NGX_CONF_ERROR;
    }
    /* 初始化变量 使用默认值*/
    ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
    cycle->connection_n = ecf->connections;

    ngx_conf_init_uint_value(ecf->use, module->ctx_index);

    event_module = module->ctx;
    ngx_conf_init_ptr_value(ecf->name, event_module->name->data);

    ngx_conf_init_value(ecf->multi_accept, 0);
    ngx_conf_init_value(ecf->accept_mutex, 0);
    ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);

    return NGX_CONF_OK;
}

 对于create_conf和init_conf来说,这两个回调函数是围绕ngx_event_conf_t进行分配、初始化。接下来看一下,init_module回调函数实现:

/**
 * Event模块初始化
 */
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
    void ***cf;
    u_char *shared;
    size_t size, cl;
    ngx_shm_t shm;
    ngx_time_t *tp;
    ngx_core_conf_t *ccf;
    ngx_event_conf_t *ecf;
    /* 获取顶级类型的conf 再获取子级conf */
    cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
    ecf = (*cf)[ngx_event_core_module.ctx_index];

    if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER)
    {
        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                      "using the \"%s\" event method", ecf->name);
    }

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

    ngx_timer_resolution = ccf->timer_resolution;

#if !(NGX_WIN32)
    {
        ngx_int_t limit;
        struct rlimit rlmt;

        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "getrlimit(RLIMIT_NOFILE) failed, ignored");
        }
        else
        {
            if (ecf->connections > (ngx_uint_t)rlmt.rlim_cur && (ccf->rlimit_nofile == NGX_CONF_UNSET || ecf->connections > (ngx_uint_t)ccf->rlimit_nofile))
            {
                limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ? (ngx_int_t)rlmt.rlim_cur : ccf->rlimit_nofile;

                ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                              "%ui worker_connections exceed "
                              "open file resource limit: %i",
                              ecf->connections, limit);
            }
        }
    }
#endif /* !(NGX_WIN32) */

    if (ccf->master == 0)
    {
        return NGX_OK;
    }

    if (ngx_accept_mutex_ptr)
    {
        return NGX_OK;
    }

    /* 设置为缓存大小,提高读写 */
    /* cl should be equal to or greater than cache line size */
    cl = 128;
    /* 分配共享内存 */
    size = cl    /* ngx_accept_mutex */
           + cl  /* ngx_connection_counter */
           + cl; /* ngx_temp_number */

#if (NGX_STAT_STUB)

    size += cl    /* ngx_stat_accepted */
            + cl  /* ngx_stat_handled */
            + cl  /* ngx_stat_requests */
            + cl  /* ngx_stat_active */
            + cl  /* ngx_stat_reading */
            + cl  /* ngx_stat_writing */
            + cl; /* ngx_stat_waiting */

#endif
    /* 分配共享内存 调用mmap */
    shm.size = size;
    ngx_str_set(&shm.name, "nginx_shared_zone");
    shm.log = cycle->log;

    if (ngx_shm_alloc(&shm) != NGX_OK)
    {
        return NGX_ERROR;
    }

    shared = shm.addr;
    /* 初始化共享内存 共享内存主要用进程间通信 */
    ngx_accept_mutex_ptr = (ngx_atomic_t *)shared;
    ngx_accept_mutex.spin = (ngx_uint_t)-1; //对于解决惊群问题 spin设置为-1 保证进程不睡眠

    if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *)shared,
                         cycle->lock_file.data) != NGX_OK)
    {
        return NGX_ERROR;
    }

    ngx_connection_counter = (ngx_atomic_t *)(shared + 1 * cl);

    (void)ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

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

    ngx_temp_number = (ngx_atomic_t *)(shared + 2 * cl);

    tp = ngx_timeofday();

    ngx_random_number = (tp->msec << 16) + ngx_pid;

#if (NGX_STAT_STUB)

    ngx_stat_accepted = (ngx_atomic_t *)(shared + 3 * cl);
    ngx_stat_handled = (ngx_atomic_t *)(shared + 4 * cl);
    ngx_stat_requests = (ngx_atomic_t *)(shared + 5 * cl);
    ngx_stat_active = (ngx_atomic_t *)(shared + 6 * cl);
    ngx_stat_reading = (ngx_atomic_t *)(shared + 7 * cl);
    ngx_stat_writing = (ngx_atomic_t *)(shared + 8 * cl);
    ngx_stat_waiting = (ngx_atomic_t *)(shared + 9 * cl);

#endif

    return NGX_OK;
}

该函数主要是设置共享内存,因为Nginx是单线程多进程模式,为了进程间通信,此处采用共享内存方式。共享内存主要实现功能有:互斥锁,全局数据统计(例如统计模块)。下面来看一下init_process回调函数实现,由于该函数实现代码较长,这里分批实现:

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

#if (NGX_WIN32)

    /*
     * disable accept mutex on win32 as it may cause deadlock if
     * grabbed by a process which can't accept connections
     */

    ngx_use_accept_mutex = 0;

#endif
    /* 初始化全局队列 保存接收到网络事件 */
    ngx_queue_init(&ngx_posted_accept_events); //保存accept事件 优先级高
    ngx_queue_init(&ngx_posted_events); //除accept事件以外的 优先级低
    /* 初始化定时器红黑树 */
    if (ngx_event_timer_init(cycle->log) == NGX_ERROR)
    {
        return NGX_ERROR;
    }

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

        /**
         * 在任何操作系统下 运行Nginx 网络模型始终有一个 所以下面直接break 
         * linux系统下面是epoll模型,init回调方法是ngx_epoll_init
         */
        module = cycle->modules[m]->ctx;            
        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK)
        {
            /* fatal */
            exit(2);
        }

        break;
    }

 这部分代码就是初始化一些数据,其中最终需要是调用init回调函数。在注释中也写的比较明确,任何网络模型(epoll,select,devpoll等)都需要实现init函数。在linux环境下使用的网络模型是epoll,init回调函数是ngx_epoll_init。注:ngx_epoll_init函数主要工作是创建epoll对象,开启事件驱动。

    /**
     * nginx.conf开启毫秒定时器精度
     */
    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT))
    {
        struct sigaction sa;
        struct itimerval itv;
        /* 注册信号 */
        ngx_memzero(&sa, sizeof(struct sigaction));
        sa.sa_handler = ngx_timer_signal_handler;
        sigemptyset(&sa.sa_mask);

        if (sigaction(SIGALRM, &sa, NULL) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigaction(SIGALRM) failed");
            return NGX_ERROR;
        }

        itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
        itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
        itv.it_value.tv_sec = ngx_timer_resolution / 1000;
        itv.it_value.tv_usec = (ngx_timer_resolution % 1000) * 1000;
        //启动定时器 当超时后会产生SIGALRM信号
        if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "setitimer() failed");
        }
    }
    //设置门限值
    if (ngx_event_flags & NGX_USE_FD_EVENT)
    {
        struct rlimit rlmt;

        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "getrlimit(RLIMIT_NOFILE) failed");
            return NGX_ERROR;
        }

        cycle->files_n = (ngx_uint_t)rlmt.rlim_cur;

        cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
                                  cycle->log);
        if (cycle->files == NULL)
        {
            return NGX_ERROR;
        }
    }
    /* 创建连接池 默认1024 */
    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;
    }
    /* 初始化连接connection对象 */
    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);
    /* 可用连接connection设置 */
    cycle->free_connections = next;
    cycle->free_connection_n = cycle->connection_n;

    /**
     * for each listening socket 
     * 循环遍历listening 将listen socket 与connection对象进行绑定
     */
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++)
    {

#if (NGX_HAVE_REUSEPORT)
        if (ls[i].reuseport && ls[i].worker != ngx_worker)
        {
            continue;
        }
#endif
        /* 返回可用连接对象 这里listening socket也会占用一个connection对象 */
        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_HAVE_DEFERRED_ACCEPT)
        rev->deferred_accept = ls[i].deferred_accept;
#endif

        if (!(ngx_event_flags & NGX_USE_IOCP_EVENT))
        {
            if (ls[i].previous)
            {

                /*
                 * delete the old accept events that were bound to
                 * the old cycle read events array
                 */

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

#if (NGX_WIN32)

        ...

#else
        /* 事件发生后 处理函数 */
        rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
                                                : ngx_event_recvmsg;

#if (NGX_HAVE_REUSEPORT)

        if (ls[i].reuseport)
        {
            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR)
            {
                return NGX_ERROR;
            }

            continue;
        }

#endif

        if (ngx_use_accept_mutex)
        {
            continue;
        }

#if (NGX_HAVE_EPOLLEXCLUSIVE)

        ...

#endif
        //注册读事件 主要是用于listen监听
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR)
        {
            return NGX_ERROR;
        }

#endif
    }

 这段代码主要做两件事情:

1)开辟connection对象,具体大小由nginx.conf文件设置

2)将监听socket注册到epoll事件驱动中并且设置回调函数handler

三、总结

本篇主要介绍ngx_event_core_module模块加载以及初始化流程,在这部分代码中我们需要了解到,listenning socket是如何加到事件驱动以及Nginx对于连接对象的管理是如何实现的。下一篇介绍ngx_epoll_module

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值