Nginx 이벤트 모듈
개요
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教程有兴趣的朋友有所帮助。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











로그인 화면에 "귀하의 조직에서 PIN 변경을 요구합니다"라는 메시지가 나타납니다. 이는 개인 장치를 제어할 수 있는 조직 기반 계정 설정을 사용하는 컴퓨터에서 PIN 만료 제한에 도달한 경우 발생합니다. 그러나 개인 계정을 사용하여 Windows를 설정하는 경우 이상적으로는 오류 메시지가 나타나지 않습니다. 항상 그런 것은 아니지만. 오류가 발생한 대부분의 사용자는 개인 계정을 사용하여 신고합니다. 조직에서 Windows 11에서 PIN을 변경하도록 요청하는 이유는 무엇입니까? 귀하의 계정이 조직과 연결되어 있을 수 있으므로 이를 확인하는 것이 기본 접근 방식입니다. 도메인 관리자에게 문의하면 도움이 될 수 있습니다! 또한 잘못 구성된 로컬 정책 설정이나 잘못된 레지스트리 키로 인해 오류가 발생할 수 있습니다. 지금 바로

Windows 11은 신선하고 우아한 디자인을 전면에 내세웠습니다. 현대적인 인터페이스를 통해 창 테두리와 같은 미세한 세부 사항을 개인화하고 변경할 수 있습니다. 이 가이드에서는 Windows 운영 체제에서 자신의 스타일을 반영하는 환경을 만드는 데 도움이 되는 단계별 지침을 설명합니다. 창 테두리 설정을 변경하는 방법은 무엇입니까? +를 눌러 설정 앱을 엽니다. Windows개인 설정으로 이동하여 색상 설정을 클릭합니다. 색상 변경 창 테두리 설정 창 11" Width="643" Height="500" > 제목 표시줄 및 창 테두리에 강조 색상 표시 옵션을 찾아 옆에 있는 스위치를 토글합니다. 시작 메뉴 및 작업 표시줄에 강조 색상을 표시하려면 시작 메뉴와 작업 표시줄에 테마 색상을 표시하려면 시작 메뉴와 작업 표시줄에 테마 표시를 켭니다.

기본적으로 Windows 11의 제목 표시줄 색상은 선택한 어두운/밝은 테마에 따라 다릅니다. 그러나 원하는 색상으로 변경할 수 있습니다. 이 가이드에서는 이를 변경하고 데스크톱 환경을 개인화하여 시각적으로 매력적으로 만드는 세 가지 방법에 대한 단계별 지침을 논의합니다. 활성 창과 비활성 창의 제목 표시줄 색상을 변경할 수 있습니까? 예, 설정 앱을 사용하여 활성 창의 제목 표시줄 색상을 변경하거나 레지스트리 편집기를 사용하여 비활성 창의 제목 표시줄 색상을 변경할 수 있습니다. 이러한 단계를 알아보려면 다음 섹션으로 이동하세요. Windows 11에서 제목 표시줄 색상을 변경하는 방법은 무엇입니까? 1. 설정 앱을 사용하여 +를 눌러 설정 창을 엽니다. Windows"개인 설정"으로 이동한 다음

Windows Installer 페이지에 "OOBELANGUAGE" 문과 함께 "문제가 발생했습니다."가 표시됩니까? 이러한 오류로 인해 Windows 설치가 중단되는 경우가 있습니다. OOBE는 즉시 사용 가능한 경험을 의미합니다. 오류 메시지에서 알 수 있듯이 이는 OOBE 언어 선택과 관련된 문제입니다. 걱정할 필요가 없습니다. OOBE 화면 자체에서 레지스트리를 편집하면 이 문제를 해결할 수 있습니다. 빠른 수정 – 1. OOBE 앱 하단에 있는 “다시 시도” 버튼을 클릭하세요. 그러면 더 이상의 문제 없이 프로세스가 계속됩니다. 2. 전원 버튼을 사용하여 시스템을 강제 종료합니다. 시스템이 다시 시작된 후 OOBE가 계속되어야 합니다. 3. 인터넷에서 시스템 연결을 끊습니다. 오프라인 모드에서 OOBE의 모든 측면을 완료하세요.

작업 표시줄 축소판은 재미있을 수도 있지만 주의를 산만하게 하거나 짜증나게 할 수도 있습니다. 이 영역 위로 얼마나 자주 마우스를 가져가는지 고려하면 실수로 중요한 창을 몇 번 닫았을 수도 있습니다. 또 다른 단점은 더 많은 시스템 리소스를 사용한다는 것입니다. 따라서 리소스 효율성을 높일 수 있는 방법을 찾고 있다면 비활성화하는 방법을 알려드리겠습니다. 그러나 하드웨어 사양이 이를 처리할 수 있고 미리 보기가 마음에 들면 활성화할 수 있습니다. Windows 11에서 작업 표시줄 축소판 미리 보기를 활성화하는 방법은 무엇입니까? 1. 설정 앱을 사용하여 키를 탭하고 설정을 클릭합니다. Windows에서는 시스템을 클릭하고 정보를 선택합니다. 고급 시스템 설정을 클릭합니다. 고급 탭으로 이동하여 성능 아래에서 설정을 선택합니다. "시각 효과"를 선택하세요.

Windows 11의 디스플레이 크기 조정과 관련하여 우리 모두는 서로 다른 선호도를 가지고 있습니다. 큰 아이콘을 좋아하는 사람도 있고, 작은 아이콘을 좋아하는 사람도 있습니다. 그러나 올바른 크기 조정이 중요하다는 점에는 모두가 동의합니다. 잘못된 글꼴 크기 조정이나 이미지의 과도한 크기 조정은 작업 시 생산성을 저하시킬 수 있으므로 시스템 기능을 최대한 활용하려면 이를 사용자 정의하는 방법을 알아야 합니다. Custom Zoom의 장점: 화면의 텍스트를 읽기 어려운 사람들에게 유용한 기능입니다. 한 번에 화면에서 더 많은 것을 볼 수 있도록 도와줍니다. 특정 모니터 및 응용 프로그램에만 적용되는 사용자 정의 확장 프로필을 생성할 수 있습니다. 저사양 하드웨어의 성능을 향상시키는 데 도움이 될 수 있습니다. 이를 통해 화면의 내용을 더 효과적으로 제어할 수 있습니다. 윈도우 11을 사용하는 방법

화면 밝기는 최신 컴퓨팅 장치를 사용할 때 필수적인 부분이며, 특히 화면을 장시간 볼 때 더욱 그렇습니다. 눈의 피로를 줄이고, 가독성을 높이며, 콘텐츠를 쉽고 효율적으로 보는 데 도움이 됩니다. 그러나 설정에 따라 밝기 관리가 어려울 수 있으며, 특히 새로운 UI 변경이 적용된 Windows 11에서는 더욱 그렇습니다. 밝기를 조정하는 데 문제가 있는 경우 Windows 11에서 밝기를 관리하는 모든 방법은 다음과 같습니다. Windows 11에서 밝기를 변경하는 방법 [10가지 설명] 단일 모니터 사용자는 다음 방법을 사용하여 Windows 11에서 밝기를 조정할 수 있습니다. 여기에는 단일 모니터를 사용하는 데스크탑 시스템과 노트북이 포함됩니다. 시작하자. 방법 1: 알림 센터 사용 알림 센터에 액세스할 수 있습니다.

Windows의 정품 인증 프로세스에서 갑자기 이 오류 코드 0xc004f069가 포함된 오류 메시지가 표시되는 경우가 있습니다. 활성화 프로세스가 온라인으로 진행되더라도 Windows Server를 실행하는 일부 이전 시스템에서 이 문제가 발생할 수 있습니다. 이러한 초기 점검을 수행하고 시스템 활성화에 도움이 되지 않으면 기본 해결 방법으로 이동하여 문제를 해결하십시오. 해결 방법 - 오류 메시지와 활성화 창을 닫습니다. 그런 다음 컴퓨터를 다시 시작하십시오. Windows 정품 인증 프로세스를 처음부터 다시 시도하세요. 수정 1 – 터미널에서 활성화 cmd 터미널에서 Windows Server Edition 시스템을 활성화합니다. 1단계 – Windows Server 버전 확인 현재 사용하고 있는 W 종류를 확인해야 합니다.
