nginx启动初始化代码执行流程
字数 1177 2025-08-15 21:33:32
Nginx启动初始化及代码执行流程详解
1. Nginx事件模块初始化
Nginx的事件模块初始化主要在ngx_event_process_init函数中完成:
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) {
// 分配连接和事件内存
cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
// 初始化监听套接字
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
c = ngx_get_connection(ls[i].fd, cycle->log);
c->listening = &ls[i];
rev = c->read;
// 设置accept回调函数
rev->handler = ngx_http_accept;
ngx_add_event(rev, NGX_READ_EVENT, 0);
}
}
关键点:
- 为所有可能的连接预分配内存
- 遍历所有监听套接字,为每个监听套接字设置读事件
- 设置读事件的回调函数为
ngx_event_accept - 将读事件添加到事件监听机制中
2. 连接接受处理流程
当有新连接到达时,ngx_event_accept函数被调用:
void ngx_event_accept(ngx_event_t *ev) {
lc = ev->data;
ls = lc->listening;
// 接受新连接
s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
// 获取连接对象
c = ngx_get_connection(s, ev->log);
// 添加连接事件
ngx_add_conn(c);
// 调用监听器的handler处理新连接
ls->handler(c);
}
关键点:
- 调用系统accept接受新连接
- 通过
ngx_get_connection获取连接对象 - 调用监听器配置的handler处理新连接
3. HTTP模块初始化流程
HTTP模块的初始化从ngx_http_block开始:
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
// 分配main/srv/loc配置数组
ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
// 调用各模块的create配置函数
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
}
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
}
if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
}
}
// 解析http块内配置
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse(cf, NULL);
// 优化服务器配置
if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
关键点:
- 为所有HTTP模块创建配置存储空间
- 调用各模块的create配置函数初始化配置
- 解析http块内的配置指令
- 最后优化服务器配置
4. 服务器配置初始化
服务器配置初始化流程:
static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) {
// 创建server配置上下文
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
ctx->main_conf = http_ctx->main_conf;
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
// 解析server块内配置
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_SRV_CONF;
rv = ngx_conf_parse(cf, NULL);
}
static char * ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
// 解析监听地址
cscf->listen = 1;
value = cf->args->elts;
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
ngx_parse_url(cf->pool, &u);
// 添加监听配置
if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
return NGX_CONF_OK;
}
}
关键点:
- 为server块创建配置上下文
- 解析server块内的配置指令
- 解析listen指令并添加到监听配置中
5. 监听配置优化
监听配置优化流程:
static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports) {
port = ports->elts;
for (p = 0; p < ports->nelts; p++) {
addr = port[p].addrs.elts;
// 处理服务器名称
for (a = 0; a < port[p].addrs.nelts; a++) {
if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
return NGX_ERROR;
}
}
// 初始化监听
if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
return NGX_ERROR;
}
}
}
static ngx_int_t ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr) {
// 初始化哈希表
ha.pool = cf->pool;
// 添加服务器名称到哈希表
cscfp = addr->servers.elts;
for (s = 0; s < addr->servers.nelts; s++) {
name = cscfp[s]->server_names.elts;
for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server, NGX_HASH_WILDCARD_KEY);
}
}
// 初始化哈希
hash.hash = &addr->hash;
ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts);
}
static ngx_listening_t * ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) {
// 创建监听套接字
ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
// 设置连接处理函数
ls->handler = ngx_http_init_connection;
}
关键点:
- 为每个端口和地址处理服务器名称
- 创建服务器名称哈希表用于快速查找
- 创建实际的监听套接字
- 设置连接处理函数为
ngx_http_init_connection
6. 连接管理机制
Nginx的连接管理机制:
ngx_connection_t * ngx_get_connection(ngx_socket_t s, ngx_log_t *log) {
// 从空闲连接池获取连接
c = ngx_cycle->free_connections;
ngx_cycle->free_connections = c->data;
ngx_cycle->free_connection_n--;
// 初始化连接
rev = c->read;
wev = c->write;
ngx_memzero(c, sizeof(ngx_connection_t));
c->read = rev;
c->write = wev;
c->fd = s;
// 设置instance标志
rev->instance = !instance;
wev->instance = !instance;
rev->data = c;
wev->data = c;
}
void ngx_free_connection(ngx_connection_t *c) {
// 将连接放回空闲池
c->data = ngx_cycle->free_connections;
ngx_cycle->free_connections = c;
ngx_cycle->free_connection_n++;
// 清除文件描述符引用
if (ngx_cycle->files) {
ngx_cycle->files[c->fd] = NULL;
}
}
关键点:
- 使用连接池管理连接对象
- 通过instance标志防止事件混淆
- 连接重用减少内存分配开销
7. 事件处理机制
Nginx的事件处理机制:
static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
// 设置事件数据,包含instance标志
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
}
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
// 处理事件
c = event_list[i].data.ptr;
instance = (uintptr_t) c & 1;
// 检查instance是否匹配
if (c->fd == -1 || rev->instance != instance) {
// 不匹配则忽略该事件
}
}
关键点:
- 将instance标志嵌入事件数据中
- 处理事件时检查instance是否匹配
- 防止处理已关闭连接的事件
8. 第三方模块开发示例
一个简单的输出模块示例:
// 模块指令定义
static ngx_command_t ngx_http_shuchu_commands[] = {
{ ngx_string("shuchu"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_shuchu,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_shuchu_loc_conf_t, content),
NULL },
ngx_null_command
};
// 模块上下文
static ngx_http_module_t ngx_http_shuchu_ctx = {
NULL, /* preconfiguration */
ngx_http_shuchu_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_shuchu_create_loc_conf, /* create location configuration */
ngx_http_shuchu_merge_loc_conf /* merge location configuration */
};
// 模块定义
ngx_module_t ngx_http_shuchu_module = {
NGX_MODULE_V1,
&ngx_http_shuchu_ctx, /* module context */
ngx_http_shuchu_commands, /* module directives */
NGX_HTTP_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
};
关键点:
- 三大核心结构:指令集、上下文、模块定义
- 指令处理函数
- 配置创建和合并函数
- 模块类型和生命周期函数
9. 配置解析流程
Nginx配置解析流程:
char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) {
// 打开配置文件
fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
// 读取并解析token
rc = ngx_conf_read_token(cf);
// 处理指令
rc = ngx_conf_handler(cf, rc);
}
static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) {
// 遍历所有模块
for (i = 0; ngx_modules[i]; i++) {
cmd = ngx_modules[i]->commands;
// 遍历模块指令
for ( /* void */ ; cmd->name.len; cmd++) {
// 根据指令类型获取配置
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[ngx_modules[i]->index];
} else if (cmd->type & NGX_MAIN_CONF) {
conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
} else if (cf->ctx) {
confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) {
conf = confp[ngx_modules[i]->ctx_index];
}
}
// 调用指令处理函数
rv = cmd->set(cf, cmd, conf);
}
}
}
关键点:
- 递归解析配置文件
- 根据指令类型获取正确的配置上下文
- 调用模块提供的指令处理函数
- 支持多级配置块(http/server/location)
总结
Nginx的启动和初始化流程是一个复杂但高效的过程,主要包含以下几个关键阶段:
- 事件模块初始化:预分配连接和事件内存,设置监听套接字的事件处理函数
- HTTP模块初始化:创建配置结构,调用各模块的初始化函数
- 配置解析:递归解析配置文件,处理各模块的指令
- 服务器配置优化:处理服务器名称,创建哈希表,初始化监听套接字
- 连接管理:使用连接池管理连接,通过instance标志防止事件混淆
- 事件处理:高效的事件处理机制,支持高并发
理解这些流程对于开发Nginx模块和深入理解Nginx工作原理至关重要。