개요
Nginx는 이벤트 트리거에 의해 구동됩니다. 이벤트 중심 모델은 주로 이벤트 수집, 이벤트 전송 및 이벤트 처리(예: 이벤트 관리)의 세 부분으로 구성됩니다. Nginx의 작업과정에서 관심을 끄는 주요 이벤트는 다음과 같습니다. IO 네트워크 이벤트 및 타이머 이벤트입니다. 생성된 objs 디렉터리 파일에서 ngx_modules.c 파일의 내용은 다음과 같습니다. Nginx 다양한 모듈의 실행 순서 이 파일의 내용을 보면 이벤트 모듈의 실행 순서는 다음과 같습니다. Linux 시스템이므로 특정 epoll 이벤트 모듈을 지원합니다. 다음 글의 구조는 다음과 같은 순서로 작성됩니다.
extern ngx_module_t ngx_events_module; extern ngx_module_t ngx_event_core_module; extern ngx_module_t ngx_epoll_module;
이벤트 모듈 인터페이스
ngx_event_module_t 구조
Nginx에서 구조ngx_module_t는 Nginx 모듈의 가장 기본적인 인터페이스입니다. 각각의 서로 다른 유형의 모듈에는 ngx_module_t의 멤버로 구성된 이러한 유형의 모듈의 공통 인터페이스를 설명하는 특정 구조가 있습니다. ctx 관리. 이벤트 모듈의 일반 인터페이스 ngx_event_module_t 구조는 Nginx에 정의되어 있으며 src/event/ngx_event.h 파일에 정의되어 있습니다.
/* 事件驱动模型通用接口ngx_event_module_t结构体 */ typedef struct { /* 事件模块名称 */ ngx_str_t *name; /* 解析配置项前调用,创建存储配置项参数的结构体 */ void *(*create_conf)(ngx_cycle_t *cycle); /* 完成配置项解析后调用,处理当前事件模块感兴趣的全部配置 */ char *(*init_conf)(ngx_cycle_t *cycle, void *conf); /* 每个事件模块具体实现的方法,有10个方法,即IO多路复用模型的统一接口 */ ngx_event_actions_t actions; } ngx_event_module_t;
ngx_event_module_t 구조의 actions 유형은 다음과 같습니다. ngx_event_actions_t 구조인 이 멤버 구조는 이벤트 중심 모듈의 특정 메서드를 구현합니다. 이 구조는 파일에 정의되어 있습니다. src/event/ngx_event.h:
/* IO多路复用模型的统一接口 */ typedef struct { /* 添加事件,将某个描述符的某个事件添加到事件驱动机制监控描述符集中 */ ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 删除事件,将某个描述符的某个事件从事件驱动机制监控描述符集中删除 */ ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 启动对某个指定事件的监控 */ ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 禁用对某个指定事件的监控 */ ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 将指定连接所关联的描述符添加到事件驱动机制监控中 */ ngx_int_t (*add_conn)(ngx_connection_t *c); /* 将指定连接所关联的描述符从事件驱动机制监控中删除 */ ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags); /* 监控事件是否发生变化,仅用在多线程环境中 */ ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait); /* 等待事件的发生,并对事件进行处理 */ ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); /* 初始化事件驱动模块 */ ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer); /* 在退出事件驱动模块前调用该函数回收资源 */ void (*done)(ngx_cycle_t *cycle); } ngx_event_actions_t;
ngx_event_t 구조
Nginx에서 각각의 정의 특정 이벤트는 ngx_event_t 구조로 표시됩니다. ngx_event_t는 특정 이벤트를 저장하는 데 사용됩니다. 이 구조는 파일에 정의되어 있습니다. src/event/ngx_event.h:
/* 描述每一个事件的ngx_event_t结构体 */ struct ngx_event_s { /* 事件相关对象的数据,通常指向ngx_connect_t连接对象 */ void *data; /* 标志位,为1表示事件可写,即当前对应的TCP连接状态可写 */ unsigned write:1; /* 标志位,为1表示事件可以建立新连接 */ unsigned accept:1; /* used to detect the stale events in kqueue, rtsig, and epoll */ unsigned instance:1; /* * the event was passed or would be passed to a kernel; * in aio mode - operation was posted. */ /* 标志位,为1表示事件处于活跃状态 */ unsigned active:1; /* 标志位,为1表示禁用事件 */ unsigned disabled:1; /* the ready event; in aio mode 0 means that no operation can be posted */ /* 标志位,为1表示当前事件已经准备就绪 */ unsigned ready:1; /* 该标志位只用于kqueue,eventport模块,对Linux上的驱动模块没有任何意义 */ unsigned oneshot:1; /* aio operation is complete */ /* 该标志位用于异步AIO事件处理 */ unsigned complete:1; /* 标志位,为1表示当前处理的字符流已经结束 */ unsigned eof:1; /* 标志位,为1表示当前事件处理过程中出错 */ unsigned error:1; /* 标志位,为1表示当前事件已超时 */ unsigned timedout:1; /* 标志位,为1表示当前事件存在于定时器中 */ unsigned timer_set:1; /* 标志位,为1表示当前事件需要延迟处理 */ unsigned delayed:1; /* * 标志位,为1表示TCP建立需要延迟,即完成建立TCP连接的三次握手后, * 不会立即建立TCP连接,直到接收到数据包才建立TCP连接; */ unsigned deferred_accept:1; /* the pending eof reported by kqueue, epoll or in aio chain operation */ /* 标志位,为1表示等待字符流结束 */ unsigned pending_eof:1; /* 标志位,为1表示处理post事件 */ unsigned posted:1; #if (NGX_WIN32) /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */ unsigned accept_context_updated:1; #endif #if (NGX_HAVE_KQUEUE) unsigned kq_vnode:1; /* the pending errno reported by kqueue */ int kq_errno; #endif /* * kqueue only: * accept: number of sockets that wait to be accepted * read: bytes to read when event is ready * or lowat when event is set with NGX_LOWAT_EVENT flag * write: available space in buffer when event is ready * or lowat when event is set with NGX_LOWAT_EVENT flag * * iocp: TODO * * otherwise: * accept: 1 if accept many, 0 otherwise */ #if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP) int available; #else /* 标志位,在epoll事件机制中表示一次尽可能多地建立TCP连接 */ unsigned available:1; #endif /* 当前事件发生时的处理方法 */ ngx_event_handler_pt handler; #if (NGX_HAVE_AIO) #if (NGX_HAVE_IOCP) ngx_event_ovlp_t ovlp; #else /* Linux系统aio机制中定义的结构体 */ struct aiocb aiocb; #endif #endif /* epoll机制不使用该变量 */ ngx_uint_t index; /* 日志记录 */ ngx_log_t *log; /* 定时器 */ ngx_rbtree_node_t timer; /* the posted queue */ ngx_queue_t queue; /* 标志位,为1表示当前事件已经关闭 */ unsigned closed:1; /* to test on worker exit */ unsigned channel:1; unsigned resolver:1; unsigned cancelable:1; #if 0 /* the threads support */ /* * the event thread context, we store it here * if $(CC) does not understand __thread declaration * and pthread_getspecific() is too costly */ void *thr_ctx; #if (NGX_EVENT_T_PADDING) /* event should not cross cache line in SMP */ uint32_t padding[NGX_EVENT_T_PADDING]; #endif #endif };
각 이벤트 구조 ngx_event_t에서 가장 중요한 멤버는 핸들러 콜백 함수입니다. 콜백 함수는 이벤트 발생 시 처리 방법을 정의합니다. 콜백 메소드 프로토타입은 src/core/ngx_core.h 파일에 있습니다:
typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
ngx_connection_t 구조
클라이언트가 요청하는 경우 Nginx 서버가 연결 요청을 시작할 때 Nginx 서버가 수동적으로 연결을 수신하면 Nginx는 서버에 대한 수동 연결이라고 하며 수동 연결은 기본 데이터 구조로 표현됩니다. ngx_connection_t 완료되었습니다. 이 구조는 파일에 정의되어 있습니다. src/core/ngx_connection.h 매체:
/* TCP连接结构体 */ struct ngx_connection_s { /* * 当Nginx服务器产生新的socket时, * 都会创建一个ngx_connection_s 结构体, * 该结构体用于保存socket的属性和数据; */ /* * 当连接未被使用时,data充当连接池中空闲连接表中的next指针; * 当连接被使用时,data的意义由具体Nginx模块决定; */ void *data; /* 设置该链接的读事件 */ ngx_event_t *read; /* 设置该连接的写事件 */ ngx_event_t *write; /* 用于设置socket的套接字描述符 */ ngx_socket_t fd; /* 接收网络字符流的方法,是一个函数指针,指向接收函数 */ ngx_recv_pt recv; /* 发送网络字符流的方法,是一个函数指针,指向发送函数 */ ngx_send_pt send; /* 以ngx_chain_t链表方式接收网络字符流的方法 */ ngx_recv_chain_pt recv_chain; /* 以ngx_chain_t链表方式发送网络字符流的方法 */ ngx_send_chain_pt send_chain; /* * 当前连接对应的ngx_listening_t监听对象, * 当前连接由ngx_listening_t成员的listening监听端口的事件建立; * 成员connection指向当前连接; */ ngx_listening_t *listening; /* 当前连接已发生的字节数 */ off_t sent; /* 记录日志 */ ngx_log_t *log; /* 内存池 */ ngx_pool_t *pool; /* 对端的socket地址sockaddr属性*/ struct sockaddr *sockaddr; socklen_t socklen; /* 字符串形式的IP地址 */ ngx_str_t addr_text; ngx_str_t proxy_protocol_addr; #if (NGX_SSL) ngx_ssl_connection_t *ssl; #endif /* 本端的监听端口对应的socket的地址sockaddr属性 */ struct sockaddr *local_sockaddr; socklen_t local_socklen; /* 用于接收、缓存对端发来的字符流 */ ngx_buf_t *buffer; /* * 表示将当前连接作为双向连接中节点元素, * 添加到ngx_cycle_t结构体的成员 * reuseable_connections_queue的双向链表中; */ ngx_queue_t queue; /* 连接使用次数 */ ngx_atomic_uint_t number; /* 处理请求的次数 */ ngx_uint_t requests; unsigned buffered:8; unsigned log_error:3; /* ngx_connection_log_error_e */ /* 标志位,为1表示不期待字符流结束 */ unsigned unexpected_eof:1; /* 标志位,为1表示当前连接已经超时 */ unsigned timedout:1; /* 标志位,为1表示处理连接过程出错 */ unsigned error:1; /* 标志位,为1表示当前TCP连接已经销毁 */ unsigned destroyed:1; /* 标志位,为1表示当前连接处于空闲状态 */ unsigned idle:1; /* 标志位,为1表示当前连接可重用 */ unsigned reusable:1; /* 标志为,为1表示当前连接已经关闭 */ unsigned close:1; /* 标志位,为1表示正在将文件的数据发往对端 */ unsigned sendfile:1; /* * 标志位,若为1,则表示只有连接对应的发送缓冲区满足最低设置的阈值时, * 事件驱动模块才会分发事件; */ unsigned sndlowat:1; unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */ unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */ unsigned need_last_buf:1; #if (NGX_HAVE_IOCP) unsigned accept_context_updated:1; #endif #if (NGX_HAVE_AIO_SENDFILE) /* 标志位,为1表示使用异步IO方式将磁盘文件发送给网络连接的对端 */ unsigned aio_sendfile:1; unsigned busy_count:2; /* 使用异步IO发送文件时,用于待发送的文件信息 */ ngx_buf_t *busy_sendfile; #endif #if (NGX_THREADS) ngx_atomic_t lock; #endif };
요청을 처리하는 동안 Nginx 서버가 업스트림 서버에 대한 연결을 적극적으로 설정하는 경우 , 연결 설정이 완료되어 통신합니다. 이는 Nginx 서버를 기준으로 한 활성 연결이며 활성 연결은 다음과 같은 구조로 구성됩니다. ngx_peer_connection_t는 나타내지만 ngx_peer_connection_t 구조는 ngx_connection_t 구조를 캡슐화한 것이기도 합니다. 구조는 src/event/ngx_event_connect.h 파일에 정의되어 있습니다.
/* 主动连接的结构体 */ struct ngx_peer_connection_s { /* 这里是对ngx_connection_t连接结构体的引用 */ ngx_connection_t *connection; /* 远端服务器的socket的地址sockaddr信息 */ struct sockaddr *sockaddr; socklen_t socklen; /* 远端服务器的名称 */ ngx_str_t *name; /* 连接重试的次数 */ ngx_uint_t tries; /* 获取连接的方法 */ ngx_event_get_peer_pt get; /* 释放连接的方法 */ ngx_event_free_peer_pt free; /* 配合get、free使用 */ void *data; #if (NGX_SSL) ngx_event_set_peer_session_pt set_session; ngx_event_save_peer_session_pt save_session; #endif #if (NGX_THREADS) ngx_atomic_t *lock; #endif /* 本地地址信息 */ ngx_addr_t *local; /* 接收缓冲区 */ int rcvbuf; /* 记录日志 */ ngx_log_t *log; /* 标志位,为1表示connection连接已经缓存 */ unsigned cached:1; /* ngx_connection_log_error_e */ unsigned log_error:2; };
ngx_events_module 핵심 모듈
ngx_events_module 핵심 모듈 정의
ngx_events_module 모듈은 이벤트의 핵심 모듈입니다. 이 모듈의 기능은 새로운 이벤트 유형을 정의하고 각 이벤트 모듈에 대한 공통 인터페이스를 정의하는 것입니다 ngx_event_module_t 구조는 이벤트 모듈에서 생성된 구성 항목 구조를 관리하고 이벤트 클래스 구성 항목을 구문 분석합니다. 먼저 src/event/ngx_event.c 파일에 있는 모듈 정의를 살펴보세요.
/* 定义事件核心模块 */ 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 };
그 중 모듈의 구성 항목 명령 구조 ngx_events_commands 은 이 모듈의 결정된 기능입니다. 파일의 구성 항목 명령 구조ngx_events_commands src/event/ngx_event.c는 다음과 같이 정의됩니다.
/* 配置项结构体数组 */ 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 };
구성 항목 구조를 보면 이 모듈은 이벤트{...}만 지원함을 알 수 있습니다. 구성 블록이 중요하며 이벤트 모듈 관리를 위한 방법을 정의합니다. ngx_events_block ngx_events_block 방법은 src/event/ngx_event.c 파일에 정의됩니다. >
/* 管理事件模块 */ 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 */ /* 计算模块类中模块的总数,并初始化模块在模块类中的序号 */ ngx_event_max_module = 0; for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_EVENT_MODULE) { continue; } ngx_modules[i]->ctx_index = ngx_event_max_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方法,则调用该方法创建存储配置项参数的结构体 */ for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_EVENT_MODULE) { continue; } m = ngx_modules[i]->ctx; if (m->create_conf) { (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle); if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) { return NGX_CONF_ERROR; } } } /* 初始化配置项结构体cf */ pcf = *cf; cf->ctx = ctx;/* 描述事件模块的配置项结构 */ cf->module_type = NGX_EVENT_MODULE;/* 当前解析指令的模块类型 */ cf->cmd_type = NGX_EVENT_CONF;/* 当前解析指令的指令类型 */ /* 为所有事件模块解析配置文件nginx.conf中的event{}块中的指令 */ rv = ngx_conf_parse(cf, NULL); *cf = pcf; if (rv != NGX_CONF_OK) return rv; /* 遍历所有事件模块,若定义了init_conf方法,则调用该方法用于处理事件模块感兴趣的配置项 */ for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_EVENT_MODULE) { continue; } m = ngx_modules[i]->ctx; if (m->init_conf) { rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]); if (rv != NGX_CONF_OK) { return rv; } } } return NGX_CONF_OK; }
ngx_events_module 모듈 정의에는 핵심 모듈의 공통 인터페이스 구조를 가리키는 ctx 멤버가 있습니다. 코어 모듈의 일반적인 인터페이스 구조는 src/core/ngx_conf_file.h 파일에 정의되어 있습니다:
/* 核心模块的通用接口结构体 */ typedef struct { /* 模块名称 */ ngx_str_t name; /* 解析配置项前,调用该方法 */ void *(*create_conf)(ngx_cycle_t *cycle); /* 完成配置项解析后,调用该函数 */ char *(*init_conf)(ngx_cycle_t *cycle, void *conf); } ngx_core_module_t;
ngx_events_module은 핵심 모듈의 일반적인 인터페이스 구조를 정의합니다. ngx_events_module 모듈의 핵심 모듈 공통 인터페이스는 src/event/ngx_event.c 파일에 정의되어 있습니다.
/* 实现核心模块通用接口 */ static ngx_core_module_t ngx_events_module_ctx = { ngx_string("events"), NULL, /* * 以前的版本这里是NULL,现在实现了一个获取events配置项的函数,* * 但是没有什么作用,因为每个事件模块都会去获取events配置项, * 并进行解析与处理; */ ngx_event_init_conf };
Nginx 服务器在结构体 ngx_cycle_t 中定义了一个四级指针成员 conf_ctx,整个Nginx 模块都是使用该四级指针成员管理模块的配置项结构,以下events 模块为例对该四级指针成员进行简单的分析,如下图所示:
每个事件模块可以通过宏定义 ngx_event_get_conf 获取它在create_conf 中分配的结构体的指针;该宏中定义如下:
#define ngx_event_get_conf(conf_ctx, module) \ (*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index]; /* 其中 ngx_get_conf 定义如下 */ #define ngx_get_conf(conf_ctx, module) conf_ctx[module.index]
从上面的宏定义可以知道,每个事件模块获取自己在 create_conf 中分配的结构体的指针,只需在ngx_event_get_conf 传入参数 ngx_cycle_t 中的 conf_ctx 成员,并且传入自己模块的名称即可获取自己分配的结构体指针。
ngx_event_core_module 事件模块
ngx_event_core_module 模块是一个事件类型的模块,它在所有事件模块中的顺序是第一,是其它事件类模块的基础。它主要完成以下任务:
ngx_event_conf_t 结构体
ngx_event_conf_t 结构体是用来保存ngx_event_core_module 事件模块配置项参数的。该结构体在文件src/event/ngx_event.h 中定义:
/* 存储ngx_event_core_module事件模块配置项参数的结构体 ngx_event_conf_t */ typedef struct { /* 连接池中最大连接数 */ ngx_uint_t connections; /* 被选用模块在所有事件模块中的序号 */ ngx_uint_t use; /* 标志位,为1表示可批量建立连接 */ ngx_flag_t multi_accept; /* 标志位,为1表示打开负载均衡锁 */ ngx_flag_t accept_mutex; /* 延迟建立连接 */ ngx_msec_t accept_mutex_delay; /* 被使用事件模块的名称 */ u_char *name; #if (NGX_DEBUG) /* 用于保存与输出调试级别日志连接对应客户端的地址信息 */ ngx_array_t debug_connection; #endif } ngx_event_conf_t;
ngx_event_core_module 事件模块的定义
该模块在文件 src/event/ngx_event.c 中定义:
/* 事件模块的定义 */ 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 };
其中,模块的配置项指令结构
static ngx_str_t event_core_name = ngx_string("event_core"); /* 定义ngx_event_core_module 模块感兴趣的配置项 */ 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 }, /* 与上面的worker_connections配置项相同 */ { 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 }, /* 是否打开accept_mutex负载均衡锁 */ { 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负载均衡锁后,延迟处理新连接事件 */ { 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 };
其中,每个事件模块都需要实现事件模块的通用接口结构 ngx_event_module_t,
/* 根据事件模块通用接口,实现ngx_event_core_module事件模块的上下文结构 */ 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_event_module_init 和ngx_event_process_init 方法。在 Nginx 启动过程中没有使用 fork 出 worker 子进程之前,先调用 ngx_event_core_module 模块中的 ngx_event_module_init 方法,当fork 出 worker 子进程后,每一个 worker 子进程则会调用 ngx_event_process_init 方法。
ngx_event_module_init 方法在文件src/event/ngx_event.c 中定义:
/* 初始化事件模块 */ 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; /* 获取存储所有事件模块配置结构的指针数据的首地址 */ cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module); /* 获取事件模块ngx_event_core_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); } /* 获取模块ngx_core_module的配置结构 */ 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 { /* * 当前事件模块的连接数大于最大文件描述符个数, * 或者大于由配置文件nginx.conf指定的worker_rlinit_nofile设置的最大文件描述符个数时, * 出错返回; */ 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) */ /* * 模块ngx_core_module的master进程为0,表示不创建worker进程, * 则初始化到此结束,并成功返回; */ if (ccf->master == 0) { return NGX_OK; } /* * 若master不为0,且存在负载均衡锁,则表示初始化完毕,并成功返回; */ if (ngx_accept_mutex_ptr) { return NGX_OK; } /* 不满足以上两个条件,则初始化下列变量 */ /* cl should be equal to or greater than cache line size */ /* 缓存行的大小 */ cl = 128; /* * 统计需要创建的共享内存大小; * ngx_accept_mutex用于多个worker进程之间的负载均衡锁; * ngx_connection_counter表示nginx处理的连接总数; * ngx_temp_number表示在连接中创建的临时文件个数; */ size = cl /* ngx_accept_mutex */ + cl /* ngx_connection_counter */ + cl; /* ngx_temp_number */ #if (NGX_STAT_STUB) /* * 下面表示某种情况的连接数; * ngx_stat_accepted 表示已成功建立的连接数; * ngx_stat_handled 表示已获取ngx_connection_t结构并已初始化读写事件的连接数; * ngx_stat_requests 表示已被http模块处理过的连接数; * ngx_stat_active 表示已获取ngx_connection_t结构体的连接数; * ngx_stat_reading 表示正在接收TCP字符流的连接数; * ngx_stat_writing 表示正在发送TCP字符流的连接数; * ngx_stat_waiting 表示正在等待事件发生的连接数; */ 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 /* 初始化共享内存信息 */ shm.size = size; shm.name.len = sizeof("nginx_shared_zone"); shm.name.data = (u_char *) "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; /* -1表示以非阻塞模式获取共享内存锁 */ ngx_accept_mutex.spin = (ngx_uint_t) -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, %d", 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; }
ngx_event_process_init 方法在文件src/event/ngx_event.c 中定义:
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; /* 获取ngx_core_module核心模块的配置结构 */ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); /* 获取ngx_event_core_module事件核心模块的配置结构 */ ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); /* * 在事件核心模块启用accept_mutex锁的情况下, * 只有在master-worker工作模式并且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 {/* 否则关闭负载均衡锁 */ 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); ngx_queue_init(&ngx_posted_events); /* 初始化由红黑树实现的定时器 */ if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { return NGX_ERROR; } /* 根据use配置项所指定的事件模块,调用ngx_actions_t中的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; } #if !(NGX_WIN32) /* * NGX_USE_TIMER_EVENT只有在eventport和kqueue事件模型中使用, * 若配置文件nginx.conf设置了timer_resolution配置项, * 并且事件模型不为eventport和kqueue时,调用settimer方法, */ if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { struct sigaction sa; struct itimerval itv; ngx_memzero(&sa, sizeof(struct sigaction)); /* * ngx_timer_signal_handler的实现如下: * void ngx_timer_signal_handler(int signo) * { * ngx_event_timer_alarm = 1; * } * ngx_event_timer_alarm 为1时表示需要更新系统时间,即调用ngx_time_update方法; * 更新完系统时间之后,该变量设为0; */ /* 指定信号处理函数 */ sa.sa_handler = ngx_timer_signal_handler; /* 初始化信号集 */ sigemptyset(&sa.sa_mask); /* 捕获信号SIGALRM */ 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; /* 使用settimer函数发送信号 SIGALRM */ if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } } /* 对poll、/dev/poll、rtsig事件模块的特殊处理 */ 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; } } #endif /* 预分配连接池 */ 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; } i = cycle->connection_n; next = NULL; /* 按照序号,将读、写事件与连接对象对应,即设置到每个ngx_connection_t 对象中 */ 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 */ /* 为所有ngx_listening_t监听对象中的connections成员分配连接,并设置读事件的处理方法 */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { /* 为监听套接字分配连接,并设置读事件 */ 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; #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) if (ngx_event_flags & NGX_USE_IOCP_EVENT) { ngx_iocp_conf_t *iocpcf; rev->handler = ngx_event_acceptex; if (ngx_use_accept_mutex) { continue; } if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { return NGX_ERROR; } ls[i].log.handler = ngx_acceptex_log_error; iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module); if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex) == NGX_ERROR) { return NGX_ERROR; } } else { rev->handler = ngx_event_accept; if (ngx_use_accept_mutex) { continue; } if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } } #else /* 为监听端口的读事件设置处理方法ngx_event_accept */ 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 { /* 将监听对象连接的读事件添加到事件驱动模块中 */ if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } } #endif } return NGX_OK; }
参考资料:
《深入理解 Nginx 》
《nginx事件模块分析(二)》
以上就介绍了Nginx 事件模块,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。