【Nginx】ngx_event_core_module模块

本文详细解析了Nginx事件核心模块ngx_event_core_module的启动过程,包括配置项解析、事件驱动模块初始化及资源配置等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ngx_event_core_module模块属于事件模块,它是其它事件类模块的基础。它主要完成以下任务:
  • 创建连接池
  • 决定使用哪些事件驱动机制
  • 初始化将要使用的事件模块

下面分析该模块的代码。
ngx_event_core_module的ngx_command_t数组定义如下:
/* ngx_event_core_module对7个配置项感兴趣 */
static ngx_command_t  ngx_event_core_commands[] = {
    /* 一个worker进程的最大TCP连接数 */
    {
        ngx_string("worker_connections"),
        NGX_EVENT_CONF|NGX_CONF_TAKE1,
        ngx_event_connections,
        0,
        0,
        NULL
    },
    /* 同上 */
    {
        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_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
    },
    /* 启用负载均衡锁后,延迟accept_mutex_delay毫秒再处理新连接事件 */
    {
        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
    },
    /* 对指定IP的TCP连接打印debug级别的调试日志 */
    {
        ngx_string("debug_connection"),
        NGX_EVENT_CONF|NGX_CONF_TAKE1,
        ngx_event_debug_connection,
        0,
        0,
        NULL
    },
    ngx_null_command
}; 


有几个配置项的解析使用的是Nginx预设的方法,解析出来的参数存放在ngx_event_conf_t结构体中,该结构体定义如下:
/* 对应ngx_event_core_commands数组中解析配置项的7个方法 */
typedef struct {
    ngx_uint_t    connections;  /* 连接池大小 */
    ngx_uint_t    use;          /* 该模块在事件模块中的编号,也就是ngx_module_s.ctx_index */
    ngx_flag_t    multi_accept; /* 1表示一次建立尽可能多的连接 */
    ngx_flag_t    accept_mutex; /* 1表示启用负载均衡锁 */
    ngx_msec_t    accept_mutex_delay;   /* 拿不到负载均衡锁时延迟建立连接的时间 */
    u_char       *name;         /* 所选用事件模块的名字 */
#if (NGX_DEBUG)
    ngx_array_t   debug_connection; /* 调试用 */
#endif
} ngx_event_conf_t; // 存储事件类配置项的结构体 


下面再看看事件模块的通用接口ngx_event_module_t结构体的定义:
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 */
    /* 定义事件驱动模块的核心方法
     * ngx_event_core_module不负责TCP网络事件的驱动,所以无须定义这些方法
     */
    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
}; 


从上面的通用接口可以看到,ngx_event_core_module模块并没有定义10个抽象方法。因为事件驱动模块是诸如epoll、select这样具有事件驱动机制的模块来担当的,这些模块才需要提供函数接口,以便将事件添加或删除。而ngx_event_core_module模块的作用只是管理更下层的事件驱动模块,例如ngx_epoll_module、ngx_kqueue_module、ngx_poll_module等模块。

下面是整个ngx_module_t结构体的定义:
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
}; 


可以看到,该模块定义了两个函数:ngx_event_module_init和ngx_event_process_init。Nginx启动过程中,在fork出worker进程之前调用ngx_event_module_init,这个函数做一些变量的初始化;在fork出worker进程之后调用ngx_event_process_init,该函数做了较多工作,流程图如下:


此函数的代码在下方,注释中的标号和流程图中的标号相对应:
/* Nginx启动时在fork出worker子进程后调用 */
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);   // 获得存储配置项的结构体
    /* 1、确定使用负载均衡锁的条件
     * 1.1 工作在master模式
     * 1.2 配置文件中打开了负载均衡锁
     * 1.3 worker进程数量大于1
     */
    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 {
        /* 2、关闭负载均衡锁 */
        ngx_use_accept_mutex = 0;
    }
    /* 3、初始化红黑树实现的定时器 */
    if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
        return NGX_ERROR;
    }
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }
        /* ecf->use中保存了所使用的事件驱动模块的索引号 */
        if (ngx_modules[m]->ctx_index != ecf->use) {
            continue;
        }
        module = ngx_modules[m]->ctx;   /* 获得选中模块的通用接口 */
        /* 4、初始化事件驱动模块模块 */
        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
            /* fatal */
            exit(2);
        }
        break;
    }
    /* 当配置文件中设置了timer_resolution配置项,进入这个块来控制时间精度 */
    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;
        /* 5、设置时间间隔,根据配置项timer_resolution的值,
         * 每隔这么多毫秒回调ngx_timer_signal_handler方法,
         * 该方法置位ngx_event_timer_alarm标志,表示需要更新时间
         */
        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;
        /* 6、预先分配句柄 */
        cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
                                  cycle->log);
        if (cycle->files == NULL) {
            return NGX_ERROR;
        }
    }
    /* 7、预先分配连接池 */
    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
    if (cycle->connections == NULL) {
        return NGX_ERROR;
    }
    c = cycle->connections; // c指向连接池中的一个连接
    /* 8、预先分配读事件,事件个数等于分配的连接个数 */
    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
    }
    /* 9、预先分配写事件,事件个数等于分配的连接个数 */
    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--;
        /* 10、ngx_cycle_t所包含的连接池中,c指向连接池,
         * 一个连接对应一个读事件和一个写事件
         */
        c[i].data = next;   /* 以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);
    /* 11、让free_connections指向空闲链表首部 */
    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++) {
        /* 从空闲连接链表获得ngx_connection_t连接 */
        c = ngx_get_connection(ls[i].fd, cycle->log);
        if (c == NULL) {
            return NGX_ERROR;
        }
        c->log = &ls[i].log;
        c->listening = &ls[i];
        ls[i].connection = c;
        rev = c->read;  /* 连接对应的读事件 */
        rev->log = c->log;
        rev->accept = 1;
        /* 12、定义读事件消费方法,有连接请求事件时调用此方法建立新连接 */
        rev->handler = ngx_event_accept;
        if (ngx_use_accept_mutex) {
            continue;
        }
        /* 13、将监听连接的读事件添加到epoll模块 */
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }
    return NGX_OK;
} 


至此,ngx_event_core_module模块的启动工作完成,接下来的工作交由epoll事件驱动模块ngx_epoll_module负责,它负责收集、分发等事件。

参考:
《深入理解Nginx》 P305-P310.
<think>好的,用户遇到了编译错误,具体是Nginx第三方模块编译时出现的“field ‘pkt6’has incompletetype”错误。我需要根据提供的引用信息和之前的知识来解决这个问题。首先,问题出在ngx_event_udp.h文件中的pkt6字段,类型是struct in6_pktinfo,编译器认为这个结构体不完整。这可能是因为头文件没有正确包含,或者结构体定义缺失。 引用[1]提到用户升级到Nginx1.24.0后出现这个错误。可能的原因是Nginx版本更新后某些头文件的依赖关系发生了变化。in6_pktinfo结构体通常在<netinet/in.h>中定义,所以检查模块的代码是否包含了这个头文件。如果模块没有包含,编译器无法识别结构体,导致错误。接下来,查看用户提供的代码示例,ngx_json_post_module.c的第11行引用了ngx_http.h,而ngx_http_upstream.h可能间接引用了ngx_event_udp.h。如果模块代码中没有直接包含必要的网络头文件,可能需要手动添加#include<netinet/in.h>或者类似的头文件。另外,引用[2]和[3]涉及Nginx事件结构和SSL处理,可能与当前问题无关。引用[4]提到FastDFS整合Nginx时的模块编译错误,解决方法通常是版本匹配或重新安装模块。这提示用户可能需要检查第三方模块Nginx版本的兼容性,或者尝试更新模块到支持新版本Nginx的版本。 总结可能的解决方案:首先确保模块代码包含必要的头文件;其次检查Nginx和第三方模块的版本兼容性;最后,如果问题依旧,可能需要修改模块代码或应用补丁。需要逐步指导用户检查这些方面,并提供具体的操作步骤,比如在代码中添加头文件包含,或者联系模块维护者获取支持。</think>### 解决Nginx编译错误“field ‘pkt6’ has incomplete type”的分步方案 #### 问题背景 当编译第三方Nginx模块(如`ngx_json_post_module`)时,出现以下错误: ```c src/event/ngx_event_udp.h:38:27: error: field ‘pkt6’ has incomplete type 38 | struct in6_pktinfo pkt6; ``` 此问题源于`struct in6_pktinfo`的定义未被编译器正确识别[^1]。 --- #### 解决步骤 ##### 1. 确认系统头文件依赖 `struct in6_pktinfo`的定义通常位于系统头文件`<netinet/in.h>`中。需检查模块代码是否包含该头文件: ```c // 在模块代码(如ngx_json_post_module.c)的开头添加 #include <netinet/in.h> ``` 若已包含但未生效,可能是头文件路径问题,需通过编译参数指定: ```bash # 在编译Nginx时添加包含路径 ./configure --with-cc-opt="-I/usr/include/linux" ``` ##### 2. 检查Nginx版本与模块兼容性 - **Nginx 1.24.0的更新**:某些第三方模块可能未适配新版本的结构体定义,需检查模块的GitHub仓库或社区是否有补丁[^1][^4]。 - **临时回退方案**:若模块未更新,可暂时降级到Nginx 1.23.x。 ##### 3. 手动修复结构体定义(高风险) 若问题持续且模块无更新,可尝试在模块代码中**显式定义缺失的结构体**: ```c // 在模块代码中手动声明in6_pktinfo(仅限紧急情况) struct in6_pktinfo { struct in6_addr ipi6_addr; unsigned int ipi6_ifindex; }; ``` > **注意**:此方法可能导致与其他系统库的冲突,需谨慎测试。 ##### 4. 联系模块维护者 提交Issue至模块的GitHub仓库,附上完整的错误日志和Nginx版本信息,请求适配新版本Nginx。 --- #### 关联问题 1. **如何验证`struct in6_pktinfo`是否被正确定义?** 可通过以下命令检查头文件内容: ```bash grep -rn 'struct in6_pktinfo' /usr/include/ ``` 2. **Nginx升级后如何保证第三方模块兼容性?** 建议在升级前查阅模块的文档或社区讨论,确认其支持目标版本。 3. **其他类似结构体缺失错误如何解决?** 通用方法为: - 添加缺失的头文件包含 - 检查库版本兼容性 - 手动定义结构体(最后手段) --- #### 引用说明 [^1]: 此错误与Nginx 1.24.0的代码结构调整有关,部分第三方模块未及时适配新版本的头文件依赖。 [^4]: FastDFS模块的类似错误可通过版本回退解决,但需权衡功能与稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值